New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OPUS HLS support #1713
Comments
Hey @haywirez The reason is that Opus inside HLS is not really a standardized payload. At Soundcloud we were able to introduce it using custom extensions to the player frameworks we had in place (namely GStreamer and/or FFmpeg but also a MediaSource API based client similar to Hls.js :)). Extending that support to Hls.js was something I had planned to do since a while but haven't gotten too many requests about it either. What's your use case if I may ask? :) |
@tchakabam my use case is to basically 100% copy what soundcloud is doing! would love more info 😉 |
Ha! :) Well there is not much too it if the OPUS stream has been created as one piece. We initially were doing OTF-transcoding of single MP3 segments, which had us deal with codec-delays and such, we had to use overlapping windows. Tricky and ugly stuff. But I guess you just want to create on OPUS stream and segment it? Do you use an Ogg container? It would be cool to create some support for these unofficial HLS payloads. One could possibly use FLAC also if one wants lossless quality. |
@tchakabam universal support including FLAC would be brilliant! What I'm doing is just segmenting an input file straight to Opus segments with ffmpeg (I guess no Ogg container - do I need that for anything ?). Code is roughly looking like this:
I noticed a difference between the total lengths comparing mp3 and opus .m3u8 files, which seems quite disturbing to me:
vs.
Ideally I would like to be able to seamlessly chain the downloaded segments into an AudioBuffer or temp file (not sure yet) for playback. I'm trying to get to the mythical < 1s time-to-play, but then also have a way to manipulate the playback responsively (think DJ-style queing, forward scrub, rewinds...) |
Anything I can check in the codebase to move this forward? |
@haywirez that's maybe because of mp3 inherent delay, that means the mp3 encoder builds in a bit of silence at the beginning of your first segment in order to initialize itself. also when you are seeking to a point with mp3, you actually need to decode a few samples before that to actually be able to decode the sample that you want to get. If you'd want to work on this, you'd have to simply make sure that MediaSource.isTypeSupported is true for the codec string in the manifest, or that if you think it's opus (based on file extension), and in that case you can create source-buffers for Opus (see buffer-controller), and then concerning remuxing nothing needs to be done, it's plain pass-through. So in principle, this shouldn't be hard :) Only that our codebase isn't the most inviting in terms of structure and readability, but then again, we're here to answer questions. |
@tchakabam are you sure opus is supported natively? MediaSource.isTypeSupported('audio/ogg; codecs="opus"'); is MediaSource.isTypeSupported('audio/webm; codecs="opus"'); is supported though |
Seems like |
In any case, |
@tjenkinson Yes, it seems they have opted for webm packaging, which is probably a sane choice, so they don't have to maintain an ogg demuxer just for opus in Chromium. There was a time when opus/ogg was supported, but I think that has been dropped then. Or maybe that was in Firefox. |
The other obvious options is of course to use the JS opus decoder and WebAudio :) |
If you use web audio you can just give it raw opus. Webaudio has other limitations though. |
Which do you mean? :) |
On iOS for example it will be muted with the mute switch, and stop when the browser is no longer the active app. Also I tried to seamlessly switch between segments before with web audio without much success. |
Oh yeah the mobile browsers, love them :)
What do you mean exactly by that? Gapless playback across tracks of an album? |
I mean hls segments |
well you need to implement the OPUS decoding as part of the streaming pipeline. basically that would mean use the JS opus decoder (that is why you shouldn't use WebAudio API to decode), and there are a few tricks depending on how your OPUS is encoded to be aware of. it is definitely not impossible. OPUS bit-streams of the same "profile" can definitely be decoded (same decoder instance) and rendered (not need to re-init the audio device) "seamlessly". we had done it back then with GStreamr/skippy: https://github.com/soundcloud/skippyHLS, see the OPUS decoder part in |
@tjenkinson did you try to implement some kind of a parallel playback position / elapsed time tracking solution for stitching the segments together? I'm tracking this, seems like a problem as the API is not sample accurate: |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Sorry, still down on the todo list 😢 Still planning to have a proper look & attempt |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Does anyone have any pointers on how this could be added in? I'm happy to try and create a PR as this would be very handy in one of my side projects! |
@jonjomckay Step one should be defining the test content. Afaik it's as simple as generating an opus stream and cutting it on frame boundaries plus writing the m3u8 file for it. That could be done likewise with Gstreamer ( Then, there are several possibilities for playing them back: Local-transcoding to AAC or MP3, native MSE support for OPUS or WebAudio decoding (and then again, decoding using with WASM-based libOpus or native WAAPI decoding support Depending which one you choose you have to do different things. I would try to support WebAudio based decoding/playback because MSE support of Opus might stay flaky or inexistent, and transcoding to MP3 is using more than three times the CPU time you'd need to "just" decode it in the first place. So let's go the WebAudio direction: Hls.js For starters you would make sure these opus segments are passed through to the buffer-controller by making sure they are using the pass-through-remuxer (not attempt any parsing / fmp4 transmuxing on them). Once you get the opus segments into BufferController you can decode it and/or pass it to any underlying web-audio player. Finally, you need to create a "fake" HTML5 media-element API implementation that will expose at least |
@tchakabam thanks for the in-depth answer! I'll see if I can come up with something |
@jonjomckay if using ffmpeg, you will have to use the more generic segment rather than the hls muxer command to generate valid segments in formats that are different from those in the HLS spec (if I understand correctly, everything else besides aac, mp3 or ac-3). Took me some time to figure this out. Sample command:
|
Following this for interest 😄 |
Any plans to support OPUS HLS streams? What would it take? OPUS now works everywhere relevant except Safari. To illustrate, using https://video-dev.github.io/hls.js/demo/ in Chrome/FF, I'm not quite sure why this works:
https://f002.backblazeb2.com/file/songsling/hls/mp3/SYqtEZaVbjcfn7cAeTbsk9.m3u8
But this one doesn't:
https://f002.backblazeb2.com/file/songsling/hls/opus/SYqtEZaVbjcfn7cAeTbsk9.m3u8
Expected behavior
Both streams play fine
Actual behavior
Only mp3 stream plays, opus errors
Console output
The text was updated successfully, but these errors were encountered: