Showing posts with label video-streaming. Show all posts
Showing posts with label video-streaming. Show all posts

Tuesday, June 12, 2018

NodeJS video streaming transcode on the fly

Leave a Comment

I'm building a nodejs app that streams a video from the disk to the user. Thanks to this question, i have a working video feed for a "static" video file that is stored on disk, so far so good.

My problem is that i need to transcode the video on the fly, to do this i used fluent-ffmpeg and i was successful at implementing the transcoding, but the HTML5 player only shows the first 3-4s of the video, and then stops. I'm guessing the problem is the filesize, but even when i harcode it nothing changes.

Any idea ? Thanks a lot :)

var file = 'Big_Buck_Bunny_1080p_surround_FrostWire.com.mp4';     fs.stat(file, function(err, stats) {         var range = req.headers.range         if (!range) { // 416 Wrong range             return res.sendStatus(416)         }         var positions = range.replace(/bytes=/, "").split("-");         var start = parseInt(positions[0], 10);         var total = stats.size;         var end = positions[1] ? parseInt(positions[1], 10) : total - 1;         var chunksize = (end - start) + 1;          res.writeHead(206, {             "Content-Range": "bytes " + start + "-" + end + "/" + total,             "Accept-Ranges": "bytes",             "Content-Length": chunksize,             "Content-Type": "video/mp4"         })          var stream = fs.createReadStream(file, { start: start, end: end, autoclose: true })         .on("open", function() {             const ffmpegCommand = ffmpeg()                 .input(stream)                 .outputFormat('mp4')                 .outputOptions([ '-movflags faststart', '-frag_size 4096', '-cpu-used 2', '-deadline realtime', '-threads 4' ])                 .videoBitrate(640, true)                 .audioBitrate(128)                 .audioCodec('aac')                 .videoCodec('libx264')                 .output(res)                 .run()         }).on("error", function(err) {             res.end(err)         })     }) 

0 Answers

Read More

Monday, February 19, 2018

render video stream as partial content rather than full stream to chrome

Leave a Comment

We currently submit a video playback request to back end server the sends the full stream as inputstream to be played back in the browser(window). This works fine but has an added complication in that the seek function does not work in chrome. The suggested solution is to tell the webserver that it needs to accept a byte range and then deliver the stream in partial byte ranges. I am not sure if this will resolve the situation but my question is how to return the stream in byte ranges considering the following is the way it is done now:

    InputStream is= null;     is = new FileInputStream(ndirectoryFile);      ....     //(calling class request)     stream = videoWrapper.getVideo(id, address); 

If I read the file in byte ranges, do I just loop through the file but how do I send the response:

InputStream is = new ByteArrayInputStream(new byte[] { 0, 1, 2 });  ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int nRead; byte[] data = new byte[1024]; while ((nRead = is.read(data, 0, data.length)) != -1) {     buffer.write(data, 0, nRead); }  buffer.flush(); byte[] byteArray = buffer.toByteArray( 

The initial inputstream gets passed to quite a few classes along the way prior to sending the final response. Any ideas please.

EDIT:

I would like to just understand the html5 video issue with chrome. There are quite a few posts on setting the server response headers to include Accept-Ranges=, Content-length= , Content-Range= and this would tell chrome to download the byte range which will then allow seek feature to work. As the video playback seek works in firefox I should not have to change how I deliver the stream or would I? Would I still have to submit partial ranges of the video from the server? and how?

1 Answers

Answers 1

I resolved my situation by not having to change any streaming code but rather just ensure the correct range headers were applied to the response to ensure chrome handled the playback.

Accept-Ranges: bytes Content-Range: bytes 0-1025/905357 Content-Length: 905357 
Read More

Saturday, August 5, 2017

Chromecast not playing multiple HTML5 videos

Leave a Comment

I have 2 video players on single page. On desktop, everything works fine, both video players are playing videos. But when I try to cast this page to Chromecast (via Google Chrome extension or via https://demille.github.io/url-cast-receiver/), only first video player is active and playing, second video player is not working.

I tried to debug it and it looks like second video ends at video readyState=1 (first video has readyState=4)

Is there way to fix this? (I need multiple video players on single page, so using only one player and switching video files URLs is not a solution)

URL: http://iuvomedia.eu/chromecast/

2 Answers

Answers 1

If you want one video to play and other to be pre-loaded then you should make a queue of videos you want to play. Chromecast will play single video at a time and when it ends it'll automatically load the next one in the queue.

For information on autoplay and queuing you may visit https://developers.google.com/cast/docs/autoplay

Answers 2

You cannot have more than one active media element.

I mean, you can have two media elements where one plays a video that doesn't have any audio and the second one plays only audio.

You cannot have two active video or two active audio pipelines at the same time.

check here: Create multiple instances of html video object

Read More

Friday, July 7, 2017

AVPlayer libBacktraceRecording.dylib macOS App Crash

Leave a Comment

I'm currently developing a macOS application and I get a strange error from time to time. I use AVPlayer to stream a live video. Everything works fine but after some time of streaming suddenly the app crashed with the following error:

libBacktraceRecording.dylib`__gcd_queue_item_enqueue_hook_block_invoke:     0x10019fe32 <+0>:  pushq  %rbp     0x10019fe33 <+1>:  movq   %rsp, %rbp ->  0x10019fe36 <+4>:  movq   0x20(%rsi), %rax     0x10019fe3a <+8>:  movq   0x20(%rdi), %rcx     0x10019fe3e <+12>: cmpq   0x8(%rcx), %rax     0x10019fe42 <+16>: sete   %al     0x10019fe45 <+19>: popq   %rbp     0x10019fe46 <+20>: retq 

And the message in the console log looks like this:

2017-05-02 14:02:36.909336+0200 SampleApp[3460:236302] sendMessageWithCategory: Failed to get remote object proxy: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.rtcreportingd" UserInfo={NSDebugDescription=connection to service named com.apple.rtcreportingd} 2017-05-02 14:02:36.909421+0200 SampleApp[3460:236302] initWithSessionInfo: XPC connection interrupted 2017-05-02 14:02:36.909548+0200 SampleApp[3460:236302] sendMessageWithCategory: Failed to get remote object proxy: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.rtcreportingd" UserInfo={NSDebugDescription=connection to service named com.apple.rtcreportingd} (lldb)   

0 Answers

Read More

Sunday, March 26, 2017

Live video streaming from recording files over HTTP based on PHP

Leave a Comment

I want to organize live streaming from recording files over HTTP based on PHP.

INTRODUCTION: On the streaming server I writing video to local file(local_file.mpg) and when received a request from client then start streaming to it from
$start_byte = filesize("local_file.mpg")-10MB; The local_file.mpg is still writing and PHP script continue reading it and flushing.

PROBLEM: I streaming it via HTTP Range with the following headers:

header('HTTP/1.1 206 Partial Content'); header("Content-Type: video/mpeg"); header('Content-Length: '.($seek_end - $seek_start)); header('Content-Range: bytes '.$seek_start.'-'.$seek_end.'/'.$size); 

And flushing as follows:

while(!feof($fp)){     $buf_size = 1024*8;     $pos = ftell($fp);     if ($pos >= $item["to_byte"]){             fclose($fp);             break;     }      if ($pos + $buf_size > $item["to_byte"]){         $buf_size = $item["to_byte"] - $pos;     }      if ($buf_size > 0){         echo fread($fp, $buf_size);     }      flush();     ob_flush(); } 

I open it via VLC or FFplay, but it played until the time moment when the stream was requested. This is to be expected, because we determine the size of the file and provide it to requested side. But if we artificially increase a file size, for example
$size = filesize("local_file.mpg")+999999999999; it also not help, because video players requesting new data too early when it is not recorded. And also stopped play at the time moment when the stream was requested.

1. Please advice how to correct organize live streaming from recording files over HTTP based on PHP.
2. Is it possible to do it with HTTP RANGE mechanism or I should use another way?


UPDATE: Based on this question I tried the next code:

<?php  $file = "online.mpg";  function flush_buffers(){     ob_end_flush();     ob_flush();     flush();     ob_start(); }  header('Content-Type: video/mpeg'); $stream = fopen( $file, "rb" ); fseek($stream, (filesize($file)-10000000), SEEK_SET);  while(1){     $response = fread($stream, 8192);      echo $response;     flush_buffers(); }  fclose( $stream ); exit(); ?> 

And it works well via ffplay, but via VLC it played no more then 1 minute and stoped then.
Please advice how to make it work on VLC also?

1 Answers

Answers 1

Do you have a time limit for php execution ? if yes , change it to unlimited with :

set_time_limit(0); 
Read More

Thursday, July 7, 2016

Unable to stream video over a websocket to Firefox

Leave a Comment

I have written some code stream video over a websocket so a sourcebuffer which works in Chrome and Edge.

However, when I run this in Firefox, the video never plays back, just a spinning wheel animation is displayed. When I check the <video> statistics, It reads HAVE_METADATA as the ready state and NETWORK_LOADING as the network state.

The code looks follows:

<!DOCTYPE html> <html>   <head>     <meta charset="utf-8"/>   </head>   <body>     <video controls></video>     <script>       var mime = 'video/mp4; codecs="avc1.4D401E,mp4a.40.2"';       var address = 'ws://localhost:54132'        /* Media Source */        var source = new MediaSource();       var video = document.querySelector('video');       video.src = URL.createObjectURL(source);       source.addEventListener('sourceopen', sourceOpen);        /* Buffer */        var buffer;       var socket;       var queue = [];       var offset = -1;       var timescale;        // When the media source opens:       function sourceOpen() {         buffer = source.addSourceBuffer(mime);         buffer.addEventListener('updateend', processQueue);          socket = new WebSocket(address);         socket.binaryType = 'arraybuffer';         socket.onmessage = onMessage;        }        // When more data is received.       function onMessage(event) {         queue.push(event.data);         processQueue();       }        // Process queue if possible.       function processQueue() {         if ((queue.length == 0) || (buffer.updating)) {           return;         }          var data = queue.shift();         if (offset === -1) {           var parsed = parseMP4(data);           if (parsed.hasOwnProperty('moov')) {             timescale = parsed.moov.mvhd.timescale;           } else if (parsed.hasOwnProperty('moof')) {             offset = 0 - (parsed.moof.traf[0].tfdt.baseMediaDecodeTime / this.timescale - 0.4);             buffer.timestampOffset = offset;           }         }          // console.log('appending ' + data.byteLength + ' bytes');         buffer.appendBuffer(data);       }        // Parse out the offset.       function parseMP4(data) {         // SNIP for brevity       }     </script>   </body> </html> 

2 Answers

Answers 1

Could not reproduce <video> element not playing at firefox 47.

Merged approaches at Mocking Websocket Message Events to create mock WebSocket events; bufferAll.html demo at Let's Make a Netflix An Intro to Streaming Media on the Web for MediaSource usage pattern.

Included <progress> and progress event to notify user of media loading status.

<!DOCTYPE html>  <html>    <head>      <meta charset="utf-8"/>    </head>    <body>      <progress min="0" value="0"></progress><br><label></label><br>      <video controls></video>      <script>          // http://nickdesaulniers.github.io/netfix/demo/bufferAll.html          // http://jsfiddle.net/adamboduch/JVfkt/          // The global web socket.              var sock, sourceBuffer;          sock = new WebSocket( "ws://mock" );          sock.onerror = function(e) {            console.log("sock error", e)          }          // This is unchanging production code that doesn"t know          // we"re mocking the web socket.          sock.onmessage = function( e ) {            console.log("socket message", e.data);            sourceBuffer.appendBuffer(e.data);          };         var video = document.querySelector("video");        var progress = document.querySelector("progress");        var label = document.querySelector("label");        var assetURL = "http://nickdesaulniers.github.io/netfix/"                       + "demo/frag_bunny.mp4";        // Need to be specific for Blink regarding codecs        // ./mp4info frag_bunny.mp4 | grep Codec        var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';          if ("MediaSource" in window             && MediaSource.isTypeSupported(mimeCodec)) {          var mediaSource = new MediaSource;          //console.log(mediaSource.readyState); // closed          video.src = URL.createObjectURL(mediaSource);          mediaSource.addEventListener("sourceopen", sourceOpen);        } else {          console.error("Unsupported MIME type or codec: ", mimeCodec);        }        video.addEventListener("canplay", function() {          alert("video canplay")        })        function sourceOpen (_) {          //console.log(this.readyState); // open          var mediaSource = this;          sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);          fetchAB(assetURL, function (buf) {            sourceBuffer.addEventListener("updateend", function (event) {            console.log("sourceBuffer updateend event;"                        + "mediaSource.readyState:"                       , mediaSource.readyState);              // mediaSource.endOfStream();              // video.play();              // console.log(mediaSource.readyState); // ended            });                      });        };        // mock `WebSocket` message        function fetchAB (url, cb) {          var xhr = new XMLHttpRequest;          xhr.open("get", url);          var file = url.split("/").pop();          xhr.responseType = "arraybuffer";          xhr.onload = function () {            // mock `WebSocket` message            sock.dispatchEvent( new MessageEvent( "message", {              data: xhr.response          }));          console.log("video sent to sock", sock);          cb();          };          xhr.onprogress = function(e) {             progress.max = e.total;             progress.value = e.loaded;             label.innerHTML = "loading " + file + " ...<br>"                               + e.loaded + " of "                                + e.total + " bytes loaded";          }          xhr.send();        };        </script>    </body>    </html>

plnkr http://plnkr.co/edit/RCIqDXTB2BL3lec9bhfz

Answers 2

<!DOCTYPE html> <html>   <head>     <meta charset="utf-8"/>   </head>   <body>     <progress min="0" value="0"></progress><br><label></label><br>     <video controls></video>     <script>         // http://nickdesaulniers.github.io/netfix/demo/bufferAll.html         // http://jsfiddle.net/adamboduch/JVfkt/         // The global web socket.             var sock, sourceBuffer;         sock = new WebSocket( "ws://mock" );         sock.onerror = function(e) {           console.log("sock error", e)         }         // This is unchanging production code that doesn"t know         // we"re mocking the web socket.         sock.onmessage = function( e ) {           console.log("socket message", e.data);           sourceBuffer.appendBuffer(e.data);         };        var video = document.querySelector("video");       var progress = document.querySelector("progress");       var label = document.querySelector("label");       var assetURL = "http://nickdesaulniers.github.io/netfix/"                      + "demo/frag_bunny.mp4";       // Need to be specific for Blink regarding codecs       // ./mp4info frag_bunny.mp4 | grep Codec       var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';        if ("MediaSource" in window            && MediaSource.isTypeSupported(mimeCodec)) {         var mediaSource = new MediaSource;         //console.log(mediaSource.readyState); // closed         video.src = URL.createObjectURL(mediaSource);         mediaSource.addEventListener("sourceopen", sourceOpen);       } else {         console.error("Unsupported MIME type or codec: ", mimeCodec);       }       video.addEventListener("canplay", function() {         alert("video canplay")       })       function sourceOpen (_) {         //console.log(this.readyState); // open         var mediaSource = this;         sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);         fetchAB(assetURL, function (buf) {           sourceBuffer.addEventListener("updateend", function (event) {           console.log("sourceBuffer updateend event;"                       + "mediaSource.readyState:"                      , mediaSource.readyState);             // mediaSource.endOfStream();             // video.play();             // console.log(mediaSource.readyState); // ended           });          });       };       // mock `WebSocket` message       function fetchAB (url, cb) {         var xhr = new XMLHttpRequest;         xhr.open("get", url);         var file = url.split("/").pop();         xhr.responseType = "arraybuffer";         xhr.onload = function () {           // mock `WebSocket` message           sock.dispatchEvent( new MessageEvent( "message", {             data: xhr.response         }));         console.log("video sent to sock", sock);         cb();         };         xhr.onprogress = function(e) {            progress.max = e.total;            progress.value = e.loaded;            label.innerHTML = "loading " + file + " ...<br>"                              + e.loaded + " of "                               + e.total + " bytes loaded";         }         xhr.send();       };       </script>   </body>   </html> 
Read More

Wednesday, April 20, 2016

How to send video streaming to WOWZA server using RTSP from iOS app?

Leave a Comment

I am new in capturing video(AVFoundation) and WOWZA server.

I have checked many solutions but didn't find any tutorials or libraries on "How to work with WOWZA server using RTSP". I found one project VideoCore on GitHub but it works only on RTMP. They have suggested answer on the RTSP problem, USE live555 protocol. But i hardly understand c++ code.

My Question:

  • How do i get video stream from AVFoundation? Right now i am getting file not stream data using AVCaptureFileOutputRecordingDelegate:

captureOutput(_: didFinishRecordingToOutputFileAtURL:, fromConnections: error:)

But i need stream output data so i can send to WOWZA server.

  • How to convert video to encoded h.264 stream?
  • How to integrate live555 library to VideoCapture class for generate RTSP URL?
  • How to send that video to WOWZA using RTSP protocol?

Please, Suggest me with any library or sample code in your answer.

0 Answers

Read More

Tuesday, April 12, 2016

HLS 'EVENT' playlists failing to start in players

Leave a Comment

I have HLS playlists that look like this:

#EXTM3U #EXT-X-VERSION:3 #EXT-X-PLAYLIST-TYPE:EVENT #EXT-X-TARGETDURATION:10 #EXT-X-ALLOW-CACHE:NO #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:9.97667, https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence0.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence1.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence2.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence3.ts 

They are of EVENT type, meaning, chunks are appended as they become available, and when all chunks are there, an #EXT-X-ENDLIST tag is appended at the end.

So when all chunks are uploaded, we end up with a playlist that looks something like:

#EXTM3U #EXT-X-VERSION:3 #EXT-X-PLAYLIST-TYPE:EVENT #EXT-X-TARGETDURATION:10 #EXT-X-ALLOW-CACHE:NO #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence0.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence1.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence2.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence3.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence4.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence5.ts #EXT-X-ENDLIST 

We are seeing odd behaviour in all our clients. If you open the m3u8 playlist in iOS and Safari when the first chunk (or even after say, 3 chunks) are uploaded, the player will start playing the video as it should. Occasionally it will stop however, and be unable to resume. More often than not, it won't even begin playing.

Fully formed playlists (i.e. with an #EXT-X-ENDLIST tag) play perfectly. It's just when the playlist is partially done.

We have tried a variety of players: Quicktime, Safari, iOS, VLC, Flowplayer etc. All have a variety of issues, but this is the most pressing.

Any insight into where to look in solving this problem would be greatly appreciated.

Edit: We have tried HLS.js and it plays perfectly. Such a nice user experience too

Edit 2: To reproduce, I recommend having some sort of local HTTP server (I use python -m SimpleHTTPServer serving up a playlist above. Then literally append the files to the playlist to simulate the uploading of files, and watch the players break.

Edit 3: Okay, I have built a simple testing tool to observe the behaviour. https://github.com/dbousamra/m3u8-example Run node app.js and then try and open http://localhost:3001/playlist.m3u8 in Safari or whatever player you want. It should play fine, as it is a complete playlist.**

If however, you add a query param ?start=<some unix timestamp>, it will simulate appending of events, 1 chunk every 6 seconds, from that timestamp, until all chunks are done, at which point it will append an #EXT-X-ENDLIST line.

Example URL: http://localhost:3001/playlist.m3u8?start=1460092250872

Edit 5: I've got it up on Heroku now: http://guarded-mesa-71212.herokuapp.com/playlist.m3u8?start=

3 Answers

Answers 1

Here is what happened:

#EXTM3U #EXT-X-VERSION:6 #EXT-X-PLAYLIST-TYPE:EVENT #EXT-X-TARGETDURATION:11 #EXT-X-ALLOW-CACHE:NO #EXT-X-MEDIA-SEQUENCE:0 

if you return the above file, safari will not request the next file at all, the playing just dead.

#EXTM3U #EXT-X-VERSION:6 #EXT-X-PLAYLIST-TYPE:EVENT #EXT-X-TARGETDURATION:11 #EXT-X-ALLOW-CACHE:NO #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:9.999367, https://cammy-bucket-staging-sydney.s3.amazonaws.com/9fc1a264af66e8acb04953bc6634fb6e.ts 

if you return the above, safari will request next file around 11/2 seconds, playing will not start at this point.

#EXTM3U #EXT-X-VERSION:6 #EXT-X-PLAYLIST-TYPE:EVENT #EXT-X-TARGETDURATION:11 #EXT-X-ALLOW-CACHE:NO #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:9.999367, https://cammy-bucket-staging-sydney.s3.amazonaws.com/9fc1a264af66e8acb04953bc6634fb6e.ts #EXTINF:9.968911, https://cammy-bucket-staging-sydney.s3.amazonaws.com/3e52720b320379de8afc940c3d1b7d34.ts 

if you return the above, safari will start playing because the available media 9.999367+9.968911 is great than EXT-X-TARGETDURATION, and you will see another request around 9.999367+9.968911+11/2, it's all about timing!

Answers 2

The #EXT-X-DISCONTINUITY tag is used to indicate changes in file format, encoding parameters, number of tracks, and so on. If the segments in the playlist are identical in regard to these things, you can remove the #EXT-X-DISCONTINUITY tags from the playlist - you don't need them.

Some clients may not be compatible with version 6 of the protocol. You don't appear to be using any version 6 specific features so try setting the version number to 3 to see if that helps.

Answers 3

try something like this

Simple Media Playlist file

   #EXTM3U    #EXT-X-VERSION:3    #EXT-X-TARGETDURATION:5220    #EXTINF:5219.2,    http://media.example.com/entire.ts    #EXT-X-ENDLIST 

Sliding Window Media Playlist, using HTTPS

 #EXTM3U    #EXT-X-VERSION:3    #EXT-X-TARGETDURATION:8    #EXT-X-MEDIA-SEQUENCE:2680     #EXTINF:7.975,    https://priv.example.com/fileSequence2680.ts    #EXTINF:7.941,    https://priv.example.com/fileSequence2681.ts    #EXTINF:7.975,    https://priv.example.com/fileSequence2682.ts 

Playlist file with encrypted media segments

#EXTM3U    #EXT-X-VERSION:3    #EXT-X-MEDIA-SEQUENCE:7794    #EXT-X-TARGETDURATION:15     #EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=52"     #EXTINF:2.833,    http://media.example.com/fileSequence52-A.ts    #EXTINF:15.0,    http://media.example.com/fileSequence52-B.ts    #EXTINF:13.333,    http://media.example.com/fileSequence52-C.ts     #EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=53"     #EXTINF:15.0,    http://media.example.com/fileSequence53-A.ts 

Master Playlist file

#EXTM3U    #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000    http://example.com/low.m3u8    #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2560000    http://example.com/mid.m3u8    #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7680000    http://example.com/hi.m3u8    #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS="mp4a.40.5"    http://example.com/audio-only.m3u8 

Master Playlist with I-Frames In this example, the PROGRAM-ID attributes have been left out:

#EXTM3U    #EXT-X-STREAM-INF:BANDWIDTH=1280000    low/audio-video.m3u8    #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=86000,URI="low/iframe.m3u8"    #EXT-X-STREAM-INF:BANDWIDTH=2560000    mid/audio-video.m3u8    #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=150000,URI="mid/iframe.m3u8"    #EXT-X-STREAM-INF:BANDWIDTH=7680000    hi/audio-video.m3u8    #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=550000,URI="hi/iframe.m3u8"    #EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5"    audio-only.m3u8 

Master Playlist with Alternative audio In this example, the PROGRAM-ID attributes have been left out. The CODECS attributes have been condensed for space. A '\' is used to indicate that the tag continues on the following line with whitespace removed:

   #EXTM3U    #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="English", \       DEFAULT=YES,AUTOSELECT=YES,LANGUAGE="en", \       URI="main/english-audio.m3u8"    #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Deutsch", \       DEFAULT=NO,AUTOSELECT=YES,LANGUAGE="de", \       URI="main/german-audio.m3u8"    #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Commentary", \       DEFAULT=NO,AUTOSELECT=NO,URI="commentary/audio-only.m3u8"    #EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS="...",AUDIO="aac"    low/video-only.m3u8    #EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="...",AUDIO="aac"    mid/video-only.m3u8    #EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS="...",AUDIO="aac"    hi/video-only.m3u8    #EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5",AUDIO="aac"    main/english-audio.m3u8 

Master Playlist with Alternative video

#EXTM3U    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Main", \       DEFAULT=YES,URI="low/main/audio-video.m3u8"    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Centerfield", \       DEFAULT=NO,URI="low/centerfield/audio-video.m3u8"    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Dugout", \       DEFAULT=NO,URI="low/dugout/audio-video.m3u8"     #EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS="...",VIDEO="low"    low/main/audio-video.m3u8     #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Main", \       DEFAULT=YES,URI="mid/main/audio-video.m3u8"    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Centerfield", \       DEFAULT=NO,URI="mid/centerfield/audio-video.m3u8"    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Dugout", \       DEFAULT=NO,URI="mid/dugout/audio-video.m3u8"     #EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="...",VIDEO="mid"    mid/main/audio-video.m3u8     #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Main", \       DEFAULT=YES,URI="hi/main/audio-video.m3u8"    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Centerfield", \       DEFAULT=NO,URI="hi/centerfield/audio-video.m3u8"    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Dugout", \       DEFAULT=NO,URI="hi/dugout/audio-video.m3u8"     #EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS="...",VIDEO="hi"    hi/main/audio-video.m3u8     #EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5"    main/audio-only.m3u8 
Read More

Calculate .m4s segment file suffix in HTML5 video streaming when user seek to another time

Leave a Comment

I have created segments of 10 Sec for a long mp4 video using Mp4Box. The Mp4Box creates a meta info file mv_init.mp4 and and segments like mv_1.m4s, mv_2.m4s... Now i stream it using HTML5 Media Source Extension. The streaming is working properly.

But the problem is that I am unable to utilize TIME SEEKING feature of HTML5 player. When a user seek through the seekbar and switch to another time then I need to fetch the correct segment file (mv_{number}.m4s) for that currentTime. Currently I calculate the segment file suffix using the following technique:

Example:-

Video Duration: 2 Hour

Segment Size: 10 Sec

Let say the user seeked to time 25 Min, so in this case I load the following segment file:

25 Min = 25 *60 = 1500 Seconds As each segment is of 10 Sec So I divide 1500 by 10 = 1500 / 10 = 150

Segment File = mv_150.m4s

The calculation apparently seems correct but the HTML5 player then downloads many more files after mv_150.m4s to continue with the streaming.

Can anyone guide me in this case of how to correctly calculate segment file number so that after seeking the streaming run smoothly without downloading any extra files.

I used the following command to created Segments of Mp4 video:

MP4Box -dash 10000 -out video.mpd -dash-profile live -segment-name mv_ -rap video.mp4

0 Answers

Read More

Monday, April 11, 2016

Issues Streaming MP4s with Webtorrent

Leave a Comment

I'm running a Node Server that I want to stream videos from magnet links that uses WebTorrent(https://webtorrent.io/docs). When I run this, it appears as if the file is not being correctly referenced even though I have set a variable as the .mp4 file.

Just to be clear, I added in a given torrentID(magnet link) in this example to eliminate any problems I may have with express and the URLs. This magnet link leads to a download of a music video in MP4 format.

The video player is showing up, but no video is being played. I'm assuming this means that I am not trying to access the correct file. If you need to know more about WebTorrent to help me, you can read about it at https://webtorrent.io/docs

var fs = require("fs"),     http = require("http"),     url = require("url"),     path = require("path"),     request = require('request'),     host = '127.0.0.1',     port = 3000,     express = require("express"),     app = express(),     server = http.createServer(app),     WebTorrent = require('webtorrent'),     client = new WebTorrent();  app.get('/streamvid/:magLink', function(req, res){     //var torrentID = req.params.magLink;     var torrentID = 'magnet:?xt=urn:btih:84123E8B4E850A796403736E0CF02E409F0EF00B';       client.add(torrentID, function (torrent) {           var file = torrent.files[0]         file.name = 'movie.mp4';         if (req.url != "/movie.mp4") {             res.writeHead(200, { "Content-Type": "text/html" });             res.end('<video width="1024" height="768" controls> <source src="movie.mp4" type="video/mp4"> <source src="movie.ogg" type="video/ogg"> Your browser does not support the video tag. </video>');         } else {             var range = req.headers.range;             var positions = range.replace(/bytes=/, "").split("-");             var start = parseInt(positions[0], 10);          fs.stat(file, function(err, stats) {             var total = stats.size;             var end = positions[1] ? parseInt(positions[1], 10) : total - 1;             var chunksize = (end - start) + 1;          res.writeHead(206, {             "Content-Range": "bytes " + start + "-" + end + "/" + total,             "Accept-Ranges": "bytes",             "Content-Length": chunksize,             "Content-Type": "video/mp4"         });          var stream = fs.createReadStream(file, { start: start, end: end })             .on("open", function() {                 stream.pipe(res);             }).on("error", function(err) {                 res.end(err);             });         });      }      }) });  var server = http.createServer(app);  var server = app.listen(port, host);  server.on('error', function(err) {     console.log('error:' + err); });  server.on('listening', function(){     console.log('Server is Up and Running'); }); 

0 Answers

Read More

Monday, April 4, 2016

HLS 'EVENT' playlists failing to start in players

Leave a Comment

I have HLS playlists that look like this:

#EXTM3U #EXT-X-VERSION:3 #EXT-X-PLAYLIST-TYPE:EVENT #EXT-X-TARGETDURATION:10 #EXT-X-ALLOW-CACHE:NO #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:9.97667, https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence0.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence1.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence2.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence3.ts 

They are of EVENT type, meaning, chunks are appended as they become available, and when all chunks are there, an #EXT-X-ENDLIST tag is appended at the end.

So when all chunks are uploaded, we end up with a playlist that looks something like:

#EXTM3U #EXT-X-VERSION:3 #EXT-X-PLAYLIST-TYPE:EVENT #EXT-X-TARGETDURATION:10 #EXT-X-ALLOW-CACHE:NO #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence0.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence1.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence2.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence3.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence4.ts #EXTINF:9.97667,   https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence5.ts #EXT-X-ENDLIST 

We are seeing odd behaviour in all our clients. If you open the m3u8 playlist in iOS and Safari when the first chunk (or even after say, 3 chunks) are uploaded, the player will start playing the video as it should. Occasionally it will stop however, and be unable to resume. More often than not, it won't even begin playing.

Fully formed playlists (i.e. with an #EXT-X-ENDLIST tag) play perfectly. It's just when the playlist is partially done.

We have tried a variety of players: Quicktime, Safari, iOS, VLC, Flowplayer etc. All have a variety of issues, but this is the most pressing.

Any insight into where to look in solving this problem would be greatly appreciated.

Edit: We have tried HLS.js and it plays perfectly. Such a nice user experience too

Edit 2: To reproduce, I recommend having some sort of local HTTP server (I use python -m SimpleHTTPServer serving up a playlist above. Then literally append the files to the playlist to simulate the uploading of files, and watch the players break.

2 Answers

Answers 1

The #EXT-X-DISCONTINUITY tag is used to indicate changes in file format, encoding parameters, number of tracks, and so on. If the segments in the playlist are identical in regard to these things, you can remove the #EXT-X-DISCONTINUITY tags from the playlist - you don't need them.

Some clients may not be compatible with version 6 of the protocol. You don't appear to be using any version 6 specific features so try setting the version number to 3 to see if that helps.

Answers 2

try something like this

Simple Media Playlist file

   #EXTM3U    #EXT-X-VERSION:3    #EXT-X-TARGETDURATION:5220    #EXTINF:5219.2,    http://media.example.com/entire.ts    #EXT-X-ENDLIST 

Sliding Window Media Playlist, using HTTPS

 #EXTM3U    #EXT-X-VERSION:3    #EXT-X-TARGETDURATION:8    #EXT-X-MEDIA-SEQUENCE:2680     #EXTINF:7.975,    https://priv.example.com/fileSequence2680.ts    #EXTINF:7.941,    https://priv.example.com/fileSequence2681.ts    #EXTINF:7.975,    https://priv.example.com/fileSequence2682.ts 

Playlist file with encrypted media segments

#EXTM3U    #EXT-X-VERSION:3    #EXT-X-MEDIA-SEQUENCE:7794    #EXT-X-TARGETDURATION:15     #EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=52"     #EXTINF:2.833,    http://media.example.com/fileSequence52-A.ts    #EXTINF:15.0,    http://media.example.com/fileSequence52-B.ts    #EXTINF:13.333,    http://media.example.com/fileSequence52-C.ts     #EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=53"     #EXTINF:15.0,    http://media.example.com/fileSequence53-A.ts 

Master Playlist file

#EXTM3U    #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000    http://example.com/low.m3u8    #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2560000    http://example.com/mid.m3u8    #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7680000    http://example.com/hi.m3u8    #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS="mp4a.40.5"    http://example.com/audio-only.m3u8 

Master Playlist with I-Frames In this example, the PROGRAM-ID attributes have been left out:

#EXTM3U    #EXT-X-STREAM-INF:BANDWIDTH=1280000    low/audio-video.m3u8    #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=86000,URI="low/iframe.m3u8"    #EXT-X-STREAM-INF:BANDWIDTH=2560000    mid/audio-video.m3u8    #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=150000,URI="mid/iframe.m3u8"    #EXT-X-STREAM-INF:BANDWIDTH=7680000    hi/audio-video.m3u8    #EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=550000,URI="hi/iframe.m3u8"    #EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5"    audio-only.m3u8 

Master Playlist with Alternative audio In this example, the PROGRAM-ID attributes have been left out. The CODECS attributes have been condensed for space. A '\' is used to indicate that the tag continues on the following line with whitespace removed:

   #EXTM3U    #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="English", \       DEFAULT=YES,AUTOSELECT=YES,LANGUAGE="en", \       URI="main/english-audio.m3u8"    #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Deutsch", \       DEFAULT=NO,AUTOSELECT=YES,LANGUAGE="de", \       URI="main/german-audio.m3u8"    #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Commentary", \       DEFAULT=NO,AUTOSELECT=NO,URI="commentary/audio-only.m3u8"    #EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS="...",AUDIO="aac"    low/video-only.m3u8    #EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="...",AUDIO="aac"    mid/video-only.m3u8    #EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS="...",AUDIO="aac"    hi/video-only.m3u8    #EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5",AUDIO="aac"    main/english-audio.m3u8 

Master Playlist with Alternative video

#EXTM3U    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Main", \       DEFAULT=YES,URI="low/main/audio-video.m3u8"    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Centerfield", \       DEFAULT=NO,URI="low/centerfield/audio-video.m3u8"    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Dugout", \       DEFAULT=NO,URI="low/dugout/audio-video.m3u8"     #EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS="...",VIDEO="low"    low/main/audio-video.m3u8     #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Main", \       DEFAULT=YES,URI="mid/main/audio-video.m3u8"    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Centerfield", \       DEFAULT=NO,URI="mid/centerfield/audio-video.m3u8"    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Dugout", \       DEFAULT=NO,URI="mid/dugout/audio-video.m3u8"     #EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="...",VIDEO="mid"    mid/main/audio-video.m3u8     #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Main", \       DEFAULT=YES,URI="hi/main/audio-video.m3u8"    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Centerfield", \       DEFAULT=NO,URI="hi/centerfield/audio-video.m3u8"    #EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Dugout", \       DEFAULT=NO,URI="hi/dugout/audio-video.m3u8"     #EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS="...",VIDEO="hi"    hi/main/audio-video.m3u8     #EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5"    main/audio-only.m3u8 
Read More