ishahak ishahak - 6 months ago 25
iOS Question

HLS: failing to play a downloaded copy of Apple's stream

EDIT

My question is now solved (see below), and everybody can use this script to download m3u8 streams like Apple's bipbop

ORIG QUESTION

I have created this nice script to download Apple's BipBop sample HLS stream. However I'm failing to play it. If you help me solving this issue, we can all enjoy this script:

#!/bin/bash
unset PROMPT_COMMAND
set -x
URL=https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8
MANIFESTNAME=$(basename $URL)
LOCAL_MANIFEST=$MANIFESTNAME
URLPREF=$(dirname $URL)

wget $URL -O $LOCAL_MANIFEST

cat $LOCAL_MANIFEST | grep "#EXT-X-STREAM-INF:" -A1 | grep m3u8 > all_playlists.txt
cat $LOCAL_MANIFEST | grep "#EXT-X-MEDIA.*URI" | sed -e 's_.*URI=\"\(.*\)\".*_\1_' >> all_playlists.txt
#this will yield files which we already downloaded, but we want the index file itself
cat $LOCAL_MANIFEST | grep "#EXT-X-I-FRAME-STREAM-INF.*URI" | sed -e 's_.*URI=\"\(.*\)\".*_\1_' >> all_playlists.txt

while read playList; do
FOLDER=$(dirname $playList)
PL_NAME=$(basename $playList)
LOCAL_PL=$FOLDER/$PL_NAME
mkdir -p $FOLDER 2> /dev/null
PL_URL=$URLPREF/$playList

echo "PL_URL=$PL_URL"
TSL=${FOLDER}_TSList.txt
wget $PL_URL -O $LOCAL_PL
echo "LOCAL_PL=$LOCAL_PL"
cat $LOCAL_PL | grep -v "^#" | uniq > $TSL
echo "====== media list ======="
cat $TSL
echo "========================="
#TS is actually any media file
while read ts; do
TS_NAME=$URLPREF/$FOLDER/$ts
LOCAL_TS=$FOLDER/$ts
echo $TS_NAME
if [ -s $LOCAL_TS ]; then
echo "Already exists. skiping."
else
wget $TS_NAME -O $LOCAL_TS
fi
done < $TSL
rm $TSL
done < all_playlists.txt
rm all_playlists.txt
find . -name Thumbs.db -exec rm {} \;


When the stream is ready, I run an http server using Python's http server:

http-server -p 8090


and then try to play using this command:

open http://localhost:8090/bipbop_16x9_variant.m3u8 -a Safari


The http server shows normal requests:

[12:24:51] "GET /bipbop_16x9_variant.m3u8" "Mozilla/5.0 (Macintosh..."
[12:24:51] "GET /gear1/prog_index.m3u8" "Mozilla/5.0 (Macintosh..."
[12:24:52] "GET /gear1/main.ts" "Mozilla/5.0 (Macintosh..."
[12:24:52] "GET /subtitles/eng_forced/prog_index.m3u8" "Mozilla/5.0 (Macintosh..."
[12:24:52] "GET /subtitles/eng_forced/fileSequence0.webvtt" "Mozilla/5.0 (Macintosh..."
[12:24:52] "GET /gear0/prog_index.m3u8" "Mozilla/5.0 (Macintosh..."
[12:24:52] "GET /gear0/main.aac" "Mozilla/5.0 (Macintosh..."
[12:24:52] "GET /subtitles/eng_forced/fileSequence1.webvtt" "Mozilla/5.0 (Macintosh..."


But Safari is failing to play the stream, showing a "Missing Plug-in" message...

Any idea please ??? we need this stream for iOS development...

Answer

So thanks to @libertyernie's comment, this problem is now solved. I was actually suspecting this problem myself: Python's http-server module is not a correct tool for serving m3u8 streams, because it can't be configured for the right content types.

A better tool is Lighttpd.

Installation:

brew install lighttpd

Now create a config file lighttpd.conf:

server.document-root = "<full-path-no-~>/bipbop" 
server.port = 8090
mimetype.assign = (
  ".html" => "text/html", 
  ".txt" => "text/plain",
  ".jpg" => "image/jpeg",
  ".png" => "image/png",
  ".m3u8" => "application/vnd.apple.mpegurl",
  ".ts" => "video/MP2T"
)

The last two lines are critical for our success. Now we can run our stream server:

lighttpd -D -f lighttpd.conf

And now we can open http://localhost:8090/bipbop_16x9_variant.m3u8 and watch the stream.