Getting a .m3u8 stream working

Dec 7, 2013 at 5:02 AM
Edited Dec 7, 2013 at 5:04 AM
Hi,

I'm trying to use this to stream m3u8 audio to my Windows Phone and Windows 8.1 apps.

The stream I'm trying to play is:-
http://ms1.clickhere2.com/live/power98/playlist.m3u8
which leads to http://ms1.clickhere2.com/live/power98/chunklist.m3u8

When I put the stream in a sample application, in particular the Windows 8.1 sample, I get an output like:-
PlaylistSegmentManager.CheckReload (12/07/2013 13:59:41 +08:00)
PlaylistSegmentManager.CheckReload is starting ReadSubList (12/07/2013 13:59:41 +08:00)
PlaylistSegmentManager.ReadSubList (12/07/2013 13:59:41 +08:00)
PlaylistSegmentManager.UpdatePlaylist: playlist http://ms1.clickhere2.com/live/power98/chunklist.m3u8 loaded with 3 entries in 00:00:00.0680044. index: 1 dynamic: True expires: 00:00:20.3410000 (12/07/2013 13:59:41 +08:00)
MediaElement State: Opening
PlaylistSegmentManager.CheckReload (12/07/2013 13:59:41 +08:00)
BufferingQueue.ReportFlush(): Audio
'SamplePlayer.Win81.exe' (CLR v4.0.30319: Immersive Application Domain): Loaded 'C:\Windows\system32\WinMetadata\Windows.Media.winmd'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
PlaylistSegmentManager.CheckReload (12/07/2013 13:59:41 +08:00)
Media MediaStreamSourceAssigned: Idle -> Assigned at 12/07/2013 13:59:41 +08:00
++++ Starting http://ms1.clickhere2.com/live/power98/media_16592.aac?wowzasessionid=2094529330&nocache=bc4347792dcb409ab8188b660425ab6b at 12/07/2013 13:59:41 +08:00.  Total memory: 620460
'SamplePlayer.Win81.exe' (CLR v4.0.30319: Immersive Application Domain): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.WindowsRuntime.UI.Xaml\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.WindowsRuntime.UI.Xaml.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
MediaElement State: Opening
---- Completed http://ms1.clickhere2.com/live/power98/media_16592.aac?wowzasessionid=2094529330&nocache=bc4347792dcb409ab8188b660425ab6b at 12/07/2013 13:59:41 +08:00 (00:00:00.0530032).  Total memory: 718700
PlaylistSegmentManager.CheckReload (12/07/2013 13:59:41 +08:00)
Configuration AAC layer 0 profile "AAC LC (Low Complexity), 22.05kHz 1 channels" channels 1 sampling 22.05kHz length 126 CRC False
BufferingManager.UpdateBuffering: 0.18%, 00:00:00.0460000 duration, 256 size, 800644 memory
A first chance exception of type 'System.ArgumentNullException' occurred in mscorlib.dll
Command failed: Value cannot be null.
PlaylistSegmentManager.CheckReload (12/07/2013 13:59:46 +08:00)
PlaylistSegmentManager.CheckReload (12/07/2013 13:59:51 +08:00)
The thread 0x13a8 has exited with code 259 (0x103).
PlaylistSegmentManager.CheckReload (12/07/2013 13:59:56 +08:00)
The thread 0x2c20 has exited with code 259 (0x103).
The thread 0x2724 has exited with code 259 (0x103).
The thread 0x2d4c has exited with code 259 (0x103).
PlaylistSegmentManager.PlaylistExpiration (12/07/2013 14:00:01 +08:00)
PlaylistSegmentManager.PlaylistExpiration is starting ReadSubList (12/07/2013 14:00:01 +08:00)
PlaylistSegmentManager.ReadSubList (12/07/2013 14:00:01 +08:00)
PlaylistSegmentManager.CheckReload (12/07/2013 14:00:01 +08:00)
PlaylistSegmentManager.UpdatePlaylist: playlist http://ms1.clickhere2.com/live/power98/chunklist.m3u8 loaded with 6 entries in 00:00:00.0970058. index: 4 dynamic: True expires: 00:00:20.3400000 (12/07/2013 14:00:01 +08:00)
++++ Starting http://ms1.clickhere2.com/live/power98/media_16592.aac?wowzasessionid=1950220690&nocache=bc4347792dcb409ab8188b660425ab6b at 12/07/2013 14:00:01 +08:00.  Total memory: 989100
BufferingManager.UpdateBuffering: 12.89%, 00:00:03.2220000 duration, 16647 size, 1021868 memory
---- Completed http://ms1.clickhere2.com/live/power98/media_16592.aac?wowzasessionid=1950220690&nocache=bc4347792dcb409ab8188b660425ab6b at 12/07/2013 14:00:01 +08:00 (00:00:00.0290012).  Total memory: 1021868
PlaylistSegmentManager.CheckReload (12/07/2013 14:00:01 +08:00)
++++ Starting http://ms1.clickhere2.com/live/power98/media_16593.aac?wowzasessionid=1950220690&nocache=bc4347792dcb409ab8188b660425ab6b at 12/07/2013 14:00:01 +08:00.  Total memory: 1046444
---- Completed http://ms1.clickhere2.com/live/power98/media_16593.aac?wowzasessionid=1950220690&nocache=bc4347792dcb409ab8188b660425ab6b at 12/07/2013 14:00:01 +08:00 (00:00:00.0290012).  Total memory: 1079212
PlaylistSegmentManager.CheckReload (12/07/2013 14:00:01 +08:00)
++++ Starting http://ms1.clickhere2.com/live/power98/media_16594.aac?wowzasessionid=1950220690&nocache=bc4347792dcb409ab8188b660425ab6b at 12/07/2013 14:00:01 +08:00.  Total memory: 1128364
---- Completed http://ms1.clickhere2.com/live/power98/media_16594.aac?wowzasessionid=1950220690&nocache=bc4347792dcb409ab8188b660425ab6b at 12/07/2013 14:00:01 +08:00 (00:00:00.0280005).  Total memory: 1152940
PlaylistSegmentManager.CheckReload (12/07/2013 14:00:01 +08:00)
Stop clicked
Even though it is parsing and finding the correct chunks, the app remains "Opening" and no audio plays.

Please advise.
Coordinator
Dec 7, 2013 at 10:24 PM
Edited Dec 7, 2013 at 10:28 PM
The AAC configuration code seems to be getting the sampling rate wrong:
Configuration AAC layer 0 profile "AAC LC (Low Complexity), 22.05kHz 1 channels" channels 1 sampling 22.05kHz length 126 CRC False
This is what FFmpeg's ffprobe reports for the same segment:
  Duration: 00:00:10.12, bitrate: 21 kb/s
    Stream #0:0: Audio: aac, 44100 Hz, stereo, fltp, 21 kb/s
BufferingManager has some values that really shouldn't be constants. Changing SM.Media\BufferingManager.cs to the following helps, but isn't enough:
    class BufferingManager : IBufferingManager
    {
        const int BufferSizeMaximum = 8192 * 1024;
        const int BufferSizeStopBuffering = 20 * 1024;
        static readonly TimeSpan SeekEndTolerance = TimeSpan.FromMilliseconds(256);
        static readonly TimeSpan SeekBeginTolerance = TimeSpan.FromSeconds(6);
        static readonly TimeSpan BufferDurationEnableThreshold = TimeSpan.FromSeconds(3);
        static readonly TimeSpan BufferDurationThreshold = TimeSpan.FromSeconds(5);
        static readonly TimeSpan BufferDurationDisableThreshold = TimeSpan.FromSeconds(25);
        static readonly TimeSpan BufferStatusUpdatePeriod = TimeSpan.FromMilliseconds(250);
I'll take a look at the AAC handling.
Coordinator
Dec 8, 2013 at 12:26 AM
Come to think of it, you will probably need the fix in 33270dfa96b7 to avoid the ArgumentNullException you are seeing.
Coordinator
Dec 8, 2013 at 12:36 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Coordinator
Dec 8, 2013 at 12:38 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Dec 8, 2013 at 3:49 AM
Edited Dec 8, 2013 at 3:57 AM
Thanks for the reply. The above fixes allow it to play for about 8 seconds at a time. Looking forward to a further fix/update :)
Edit: Btw do I use playlist or chunklist.m3u8?
Coordinator
Dec 8, 2013 at 5:03 AM
Yeah, something is definitely wrong...

Either playlist.m3u8 or chunklist.m3u8 should work for you. The URL for the top level "playlist.m3u8" file is less likely to change, so it is probably the safer alternative. Some servers will check for a varying query string in the sub-stream URLs (or do other things, like require a cookie--see SimulatedPlayer\Program.cs for an example that handles cookies). Getting URL query string or cookie based security tokens sometimes require a bit more work; they are not always set by fetching the top-level playlist.

If you need to select a particular stream, take a look at f20618edd719.

The default behavior picks the first stream in the file. Here's a version that picks the highest bandwidth stream not over 1Mbit (assuming there is such a stream and that the top level file specifies the stream bandwidths):
    var q = from x in program.SubPrograms
            where x.Bandwidth <= 1000 * 1000
            orderby x.Bandwidth descending
            select x;

    subProgram = q.FirstOrDefault();
In this case, playlist.m3u8 only contains one stream, so sorting and such would be of somewhat limited utility.
Dec 8, 2013 at 5:08 AM
Ah, thanks.

Sorry to rush btw, but will a fix for my issue be available soon, or is there a way I could hardcode a fix? I need this to complete one of my apps >.<
Coordinator
Dec 8, 2013 at 6:56 AM
Edited Dec 9, 2013 at 9:09 AM
This is pretty much the end of my day, but I should hopefully have some time tomorrow.

In the meantime, if you want to take a look for yourself how the sampling frequency is being determined, then you can find the code here:

SM.Media.AAC\AacMediaParser process the data from the segment and uses SM.Media\AAC\AacFrameHeader.cs to parse the ADTS header. Then SM.Media\AAC\AacConfigurator's Configure() records the parameters for Windows 8.1 and uses
SM.Media\Mmreg\HeAacWaveInfo to turn it into the funky binary format used for Windows Phone's CodecPrivateData.

I'm not certain the sampling frequency might not be "wrong" as far as the rest of the system is concerned; it should certainly be reported as 44.1kHz to the Debug.WriteLine(). This may be about SBR handling since it did seem to sound a bit muffled compared to what VLC was doing with the same stream.

The other place to look is the code that breaks the stream into ADTS/AAC frames (in SM.Media\AAC\AacMediaParser.cs). It may work better as raw AAC data (type 0xFF), but phonesm is using type 0x1610. There is some #if false code at least for handling raw AAC when the data is coming from demuxed transport streams (not directly relevant here) and in AacConfigurator (note the RawAacWaveInfo stuff), but I was never able to get type 0xff to make any sound at all. For that to be tried with .aac segments, the code in AacMediaParser would need to be modified to strip the ADTS header and AacConfigurator's "#if false" needs to turn into "#if true" (at least).

After that, just remove the bugs and all should be fine. <cough>

These should provide some context:
http://wiki.multimedia.cx/index.php?title=ADTS
http://msdn.microsoft.com/en-us/library/ff426928(v=VS.96).aspx

To be clear: if the data is coming from a .ts segment, it goes through AacStreamHandler after the MPEG-2 Transport Stream demuxing. If it is coming from an .aac segment, then it is handled by AacMediaParser.
Dec 8, 2013 at 9:17 PM
I can't seem to get my stream - or any other m3u8 stream - working in the Windows Phone sample project, by the way. The media always fails to load.

I do see this error in VS though:-
Cannot open 'file:///C:/Users/RoC/Desktop/phonesm-20130921/Source/Phone/SamplePlayer.WP8/WMAppManifest.xml'. The Uri parameter must be a relative path pointing to content inside the Silverlight application's XAP package. If you need to load content from an arbitrary Uri, please see the documentation on Loading XML content using WebClient/HttpWebRequest. C:\Users\RoC\Desktop\phonesm-20130921\Source\Phone\SamplePlayer.WP8\MainPage.xaml

Ideally the stream starts working on both WP8 and Win 8.1, then my project will be 100% complete. Hope you figure out what's wrong soon ^_^
Coordinator
Dec 9, 2013 at 12:18 AM
Edited Dec 9, 2013 at 12:19 AM
I built a fresh cloned directory, phonesm-20130921, and phonesm-20130921-source, all directly from CodePlex and did not have any problems (I did update the Player Framework reference to 1.8.2.2, since that is what I have installed).

Could you clone a fresh copy into a new directory and see if you have the same problem there?

Thanks.

I'm still looking at the AAC stuff.
Coordinator
Dec 9, 2013 at 7:24 AM
d72aa4ab859d should help when playing AAC segments.
Dec 9, 2013 at 7:55 AM
Interesting, that makes the stream work, albeit a little unstable.

For me it now plays the first 10 seconds, stops for 10 seconds, then starts playing with random lag. Probably got to do (I think) with the next chunk not being loaded yet, since I do see lots of "BufferingQueue.ReportExhaustion(): Audio" in the output.
Dec 9, 2013 at 8:17 AM
As for the sample code for Windows Phone it appears I'm missing

Microsoft.Threading.Tasks
Microsoft.Threading.Tasks.Extensions
Microsoft.Threading.Tasks.Extensions.Phone
System.Net.Http
System.Net.Http.Extensions
System.Net.Http.Primitives
Coordinator
Dec 9, 2013 at 8:40 AM
Those libraries (should) get automatically restored by NuGet. If you are using VS Express, perhaps you need run NuGet.exe's command line restore?
Coordinator
Dec 9, 2013 at 8:41 AM
This may be relevant for your 10 second thing: Problems reading live streams, stops playing after 10 seconds
Dec 9, 2013 at 8:52 AM
henric wrote:
Those libraries (should) get automatically restored by NuGet. If you are using VS Express, perhaps you need run NuGet.exe's command line restore?
Ah, that restored them. Still can't get it to work though, getting loads of errors like
The name "StreamingMediaPlugin" does not exist in the namespace "clr-namespace:SM.Media.MediaPlayer;assembly=SM.Media.MediaPlayer.WP8". C:\Users\RoC\Desktop\Source\Phone\SamplePlayer.WP8\MainPage.xaml 23 17 SamplePlayer.WP8


henric wrote:
This may be relevant for your 10 second thing: Problems reading live streams, stops playing after 10 seconds
And that solved it! Thanks so much! Now time to integrate this into my app.
I don't fully understand the play_Click() method in the MainPage though, do I really need everything (e.g. programManager, segmentsFactory) to play the stream in the mediaElement?
Dec 9, 2013 at 1:29 PM
To enhance my question: If my app already has a MediaElement and I want to use that specific one, instead of doing all the MediaElementManager stuff, is it possible? I already have various methods set up for the MediaElement, and it appears things like VolumeChangedEvents and mediaElement1.Stop() stop functioning after running the MediaElementManager code.
Coordinator
Dec 10, 2013 at 4:12 AM
Unfortunately, much of the gunk in HlsView's MainPage.xaml.cs is necessary. It really needs to be consolidated into some kind of utility class. (Any volunteers out there? Do I hear crickets chirping...?) "play_Click()" is particularly ugly. The two lambda functions in MediaElementManager should let you reference your own MediaElement instead of creating a new one. Note that the complications from making sure that MediaElement is detached from the visual tree when navigating away from the page is from WP7 (where MediaElement has a glass jaw, falling over at the slightest provocation--often to the point of requiring a phone reboot). I'm not sure if it is still required for WP8.

You shouldn't need any of the SM.Media.MediaPlayer.* or SamplePlayer.* projects if you are using the MediaElement directly. They are only for use with Microsoft's Player Framework (or on WP7, SMF). If you don't have Player Framework's vsix installed (or if the version doesn't match) then you will have some errors from those projects.

Since you are playing a radio station, you might consider looking at the background audio code that miksu added. One would use the background audio instead of MediaElement for both foreground and background playback, so it shouldn't complicate things too much for you.

Also, if you did have to go to the command line NuGet, make sure that your Visual Studio NuGet is up-to-date. Check here: Tools -> Extensions and Updates... -> Updates.
Dec 10, 2013 at 10:50 AM
Thanks, I'll give WP8 a try later.

For Windows 8, would this be correct in referencing my own MediaElement:-
            _mediaElementManager = new MediaElementManager(Dispatcher,
                () =>
                {
                    var me = mediaElement1;

                    return me;
                },
                me =>
                {
                });
When I use the above it seems to work However, when I change mediaElement1's source to a non-m3u8 stream, I get:-
BufferingQueue.ReportFlush(): Audio
A first chance exception of type 'System.Threading.Tasks.TaskCanceledException' occurred in mscorlib.dll
And the stream doesn't play until I try playing it again (app doesn't crash).

Any idea why this happens? I've also noted that when I was using the method of creating a new MediaElement, when getting the m3u8 stream to stop there is a wait time before the stream stops playing, with the output:-
A first chance exception of type 'System.Threading.Tasks.TaskCanceledException' occurred in mscorlib.dll
A first chance exception of type 'System.Threading.Tasks.TaskCanceledException' occurred in mscorlib.dll
A first chance exception of type 'System.Threading.Tasks.TaskCanceledException' occurred in mscorlib.dll
A first chance exception of type 'System.Threading.Tasks.TaskCanceledException' occurred in mscorlib.dll
BufferingQueue.ReportFlush(): Audio
A first chance exception of type 'System.Threading.Tasks.TaskCanceledException' occurred in mscorlib.dll
BufferingQueue.ReportExhaustion(): Audio
BufferingManager.UpdateState start buffering: 10675199.02:48:05.4775807 duration, 0 size, 2444436 memory
  Audio count 0 size 0 newest  oldest 
Coordinator
Dec 11, 2013 at 11:36 AM
Yes, that is what I meant with the MediaElementManager (did it work for you?).

There is another discussion that is probably related to your stream switching problem. I would suggest you try both waiting for the stream to stop playing (not just telling it to stop) and setting the stream source to null before setting it to something else (perhaps experimenting with the order of those two operations).

The "TaskCancelledException"s are expected. In debug builds, all the debug output generates slows things down noticeable; "stop"s do seem to go more quickly in release builds--or perhaps it has more to do with handling exceptions while the debugger is attached?

I'm still looking into some AAC issues, so I haven't finished looking into the stream switching thing (I managed to completely break playback in SamplePlayer.WP8, but I thought that of limited utility and decided to not check in those changes).
Dec 15, 2013 at 2:36 AM
Ooh it works now after adding the code from that discussion.

Thanks so much for all your help! :D
My Windows 8.1 app is now finished, will work on getting the Windows Phone version to work now.
Sep 5, 2014 at 7:39 AM
Can any one please help me ins streaming m3u8 stream on windows phone

my mail id is shravan_pottala@yahoo.co.in
Sep 8, 2014 at 10:01 AM
Now Iam able to build and play m3u8 stream with HLS View example WP8 and WP7 on windows phone 8 and windows phone 7 devices.