-
Notifications
You must be signed in to change notification settings - Fork 150
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
MP3 frame synchronization is not adequate in some pathological cases #96
Comments
I also have about a dozen more pieces of similarly slightly garbled MP3 content (also from z0r loops), most of which seem like they are missing just the very first two bytes, wasting the entire first frame, but leaving it there as garbage that the frame sync mechanism has to skip through correctly. I'll gladly attach some more of them upon request. This one looked the most problematic though. |
Hi @torokati44, Thanks for a very detailed bug report. I think you're on the right track.
It's not the excessive
The decoder promptly errors out, but it sets the sample rate based on that first packet. The
However, by that point the decoder is already fixed to the original sample rate. In this case the erroneous header set the CRC-enabled bit to 1, and I suppose if we did check the CRC it would catch this specific case, but it's not perfect. Most of the mainstream MP3 decoders don't check CRCs by default since they can be wrong and reject otherwise working streams. Your solution makes a lot of sense to me. In the normal case it'll only add an extra 2 bytes to read (because the packet is good and we'll use the packet body). In a broken case, yes, we will have read an excessive amount of bytes, but it's not as bad as it sounds. Very few MPEG frames are that big, and the minimum I'll give it a try and report back. If you can provide a few more cases in the meantime that'd be great. Thanks! |
Of the dozen or so of other loops that Firefox has issues with[1], actually only this one turned out to be misprobed (as AAC) by Symphonia as well: To be fair, our application will most likely be able to tell the decoder that it is supposed to be MP3. [1]: And had the sound embedded in a single chunk - not split into several stream packets in the SWF - so it was easy to export. Though I guess FFmpeg would demux the rest for me as well... |
Is the AAC misdetection a problem for Ruffle, or just the first issue with the sample rate? I have a working solution for the sample rate issue that I will push after testing more. The AAC issue would require me to implement scoring since there's a sync word for an ADTS header showing up in the second file before the MP3 sync word. Currently, |
I created a new branch |
Thanks a lot! I haven't been able to test it yet, |
Ruffle uses Your WIP branch did fix the sample rate detection for both of these loops though! |
Sounds good to me! I was thinking earlier that maybe this check should go even further beyond just checking for a sync word. Perhaps it should also check to make sure the MPEG version, layer, and sample rate match between the two headers. These should be constant within a stream, though, I guess there's no real reason why they couldn't vary. EDIT: I pushed the original version since I'm not confident rejecting the streams mentioned above is a good idea. This fix will be available in v0.5 which I'll publish in about a week or two. |
symphonia-play
detects this corrupted mp3 file as stereo 22050 Hz, so it plays it back at ~4x speed.It's actually a mono 11025 Hz one, and plays correctly in VLC for example.
This data was extracted from https://z0r.de/L/z0r-de_6315.swf.
There seems to be an unusually high amount of
0xFF
bytes before the first actual frame, which most likely throws offsync_frame()
, even thoughcheck_header()
does a decent amount of sanity checking.Perhaps it should also be extended with parsing the header at any potential frame sync point, then checking whether right after the potential frame there's either the end of the data stream, or another sync sequence (frame header). This would necessitate adding a seekback buffer of (in some cases) as long as ~2.9 KiB, which should not be an issue with local file sources, but for network streams, it might add a noticeable amount of latency. It might also prevent syncing to perfectly valid frames that happen to not be directly followed by another frame... So maybe it should be a decoder option?
There might be other ways to fix the synchronization of this file, but this is what's described here: https://hydrogenaud.io/index.php?topic=85125.msg747716#msg747716
If the frame (supposedly) has CRC, that could also be used instead to rule out red herring frames.
The "correct" way might be to change the sample rate and channel setup at each decoded frame as necessary, but I'm not sure if the playback machinery (of either
symphonia-play
, or of any embedding application) is set up for that.For reference: ruffle-rs/ruffle#4273, ruffle-rs/ruffle#5231
(EDIT: The reason I'm reporting is that at the moment, https://z0r.de/6315 only has sound in Chrome, but not in Firefox, as Ruffle currently uses
decodeAudioData
to decode MP3 sounds, and the mandated-by-spec format sniffing in FF can't figure out that it is in fact supposed to be MP3 content, most likely due to the amount of garbage before the first frame. If only WebAudio/web-audio-api#7 ended up being resolved...)The text was updated successfully, but these errors were encountered: