This project has moved. For the latest updates, please go here.

Add option for offline playback?

Aug 20, 2014 at 10:07 AM
Hi,
We'd need to implement in our app some time in the future the option to play selected video in offline. It's quite easy to implement such feature for simple .mp4 files, but it is a problem for HLS streams.

So the question is have you thought about implementing support for saving target HLS stream for offline playback?
Ideally I should be able to provide .m3u8 link to the API with a StorageFolder parameter, where the API will save all required parts including metadata and encryption configuration. Then later for playing the offline video I'd just provide link to target folder or offline m3u8 file and the player will just load everything from the local storage.
Thanks
Coordinator
Aug 21, 2014 at 1:20 PM
Most of the pieces for doing this are there. If you want to download the decrypted files or want to store a live stream, then get an ISegmentReaderManagerFactory from the DI container, use it to create an ISegmentReaderManager, then use the SegementManagerReaders (there will almost certainly be exactly one of those), to find the actual segment iterator: ISegmentManagerReaders.Readers. Like so:
    public interface ISegmentManagerReaders
    {
        ISegmentManager Manager { get; }
        IAsyncEnumerable<ISegmentReader> Readers { get; }
    }
The IAsyncEnumerable works like IEnumerable, but has a MoveNextAsync() instead of MoveNext() on the IAsyncEnumerator (and, yes, MoveNextAsync() should probably have a CancellationToken argument). Create an IAsyncEnumerable, then iterate through it. Each of the resulting ISegmentReader instances--which look like this:
    public interface ISegmentReader : IDisposable
    {
        Uri Url { get; }
        bool IsEof { get; }
        Task<int> ReadAsync(byte[] buffer, int offset, int length, CancellationToken cancellationToken);
        void Close();
    }
--should be read with "ReadAsync()" until IsEof returns true. Write the resulting data to your local file. If you don't care about the files staying as separate segments, you should be able to write all the segments to the same local .TS file. Take a look at CallbackReader.cs to see how phonesm reads the segments.

Once you have copied the segments you need, call ISegmentReaderManager.StopAsync() to make sure the playlist refresh stops (in case it is a live stream).

If you prefer to do things at a lower level, you can create an instance of M3U8Parser and call one of the .Parse() extension methods. You should find the segments URLs with the segments you need to copy to local storage in the M3U8Parser.Playlist IEnumerable<>. Just be careful that you are reading the right M3U8 file since some M3U8 files refer to other M3U8 files. Note also that when you create a local M3U8 file, make sure the local playlist has links to the local copies of the segments.

To play back the result, you would need to modify HttpClientWebReaderManager/HttpClientWebReader such that it reads from local storage when the Uri.Scheme isn't HTTP or HTTPS. I'm not sure if subclassing would work or if you need to copy those classes to customize them. You should be able to register your new class with the DI container to override the default IWebReaderManager.

If you use a single .TS file, then you can play that directly. If you are using separate segments, then you will probably want to create your own M3U8 file. Note that while SM.Media.M3U8.M3U8Parser does not support writing playlists, the individual M3U8TagInstance and M3U8Uri instances do support .ToString().

Alternately, if you have something that will create (mux) MP4 files from encoded and timestamped packets (Microsoft Media Foundation?), you could create an instance of IMediaStreamFacade and use it to make a MediaStreamSource (see WP 8.1's StreamingMediaPlugin for an example). Instead of feeding it to a media player, that MediaStreamSource can be read directly to get the raw audio and video packets from your stream, then you could feed those packets to your MP4 mux. You should be able to play the resulting MP4 file from local storage.
Jan 29, 2015 at 1:05 PM
Hi,
thanks for the response. I'd just like to ask - no one is working on the offline playback right now, right? Because I'm about to implement the HttpClientWebReaderManager/HttpClientWebReader now for reading files from storage for Uris like "ms-appdata:///local/myplaylist.m3u8"
From what I already found in the source code, it should not be that hard.

Note the downloading process for saving all nested playlists, chunks of video and key files is not yet done. I've only created simple cmd tool for doing this, not sure, if this should be part of the repository.
Coordinator
Jan 29, 2015 at 1:50 PM
You're welcome.
There is no support for reading from a file right now and it is not something that I have looked at recently. However, you might find the StorageFileWebReaderManager class in the "Playing from a stream on WP8?" thread useful.
Jan 30, 2015 at 1:43 PM
Edited Jan 30, 2015 at 1:45 PM
Hi,
I've implemented first quick and dirty version capable playing files saved in local folder, right now only for Windows Phone 8:
https://phonesm.codeplex.com/SourceControl/network/forks/Necroman/offlineplayback?branch=master

The HlsView.WP8 project now also contains sample offline hls stream for test.

It needs some code cleanup, though:
  • I'm not sure where all the properties like ContentType, ActualUrl, BaseAddress and RequestUri are used and when they need to be set properly.
  • version for Windows Universal Apps, this should be easy
  • version for .NET/Desktop apps, different version is required for handling files with full path, starting with C:...
  • Silverlight version?
  • make it work as generic plugin in Autofac, now it's manually added to HttpClientWebReaderManager
Apr 22, 2015 at 8:18 AM
Edited Apr 22, 2015 at 8:19 AM
Hi, just an update to the offline HLS playback.
I've just tested the native HLS API on Windows 10 and it support offline HLS playback as well. I've just downloaded manually the m3u8 file, all .ts files plus the .key file into single folder, used "ms-appx:///hls/playlist.m3u8" as the AdaptiveMediaSource Uri and it worked like a charm.

So if you were considering adding this feature to Windows Phone Streaming Media, this might be another argument :)
Note you can check my proof of concept for offline playback in the fork in my previous post. It works fine for my app, but it might need some cleanup for general use.
Coordinator
Apr 22, 2015 at 6:48 PM
The last time I looked at this, I ran into the too-tight coupling between the web reading stuff, the content type detection and the web cache gunk (IWebCache), tried to pull apart the pieces without breaking anything, and making such a mess that I finally did a "git reset --hard origin/master".

Adding a layer of indirection that selects a reader based on the URL shouldn't be too difficult, and reading from local files isn't hard. That would be the way to deal with RTSP as well (I think).

I might take a look at this over the next few days since it doesn't involve mucking with WP8.1's background audio. The main thing I have left there is to try to use ApplicationData.Current.LocalSettings to keep the thing from losing it's place (too much) when the background audio agent falls over (typically due to out of memory exceptions).

A new beta build to replace phonesm-1.2.2 is way, way overdue, but I thought I'd wait to see what happens at BUILD next week, since there may be new revs of tools, NuGet packages, or such.

As for the Microsoft does it argument: well, couldn't one also say that it makes much of phonesm redundant and why then, "reinvent the wheel?"
Apr 27, 2015 at 9:00 AM
The phonesm library won't be redundant for at least this and the next year, until Windows 10 becomes majority OS on phones and tablets. Even now about 25% of phone users got WP8 and 20% of tablet users got still Windows 8.
Coordinator
May 2, 2015 at 5:26 AM
I'm not planning on giving up on phonesm (ok, well WP7 doesn't actually get tested anymore, other than to make sure it compiles). But most of the project has code focused on WP7-style MediaStreamSource processing. If one were starting from scratch, it would make more sense to do things through Media Foundation. Only WP7 and Silverlight need things the way they are.
May 15, 2015 at 10:10 AM
Just a note that I updated my fork with offline HLS support for WP8.1 and Win8.1 apps. Also fixed bug in ShoutcastHeaders causing exceptions in the sample HlsView app when playing the saved sample HLS stream.