Search code examples
ioshttp-live-streamingm3u8

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...


Solution

  • 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.