Closed Bug 1267887 Opened 9 years ago Closed 9 years ago

hook up rust mp4parse for opus

Categories

(Core :: Audio/Video: Playback, defect, P2)

defect

Tracking

()

RESOLVED FIXED
mozilla50
Tracking Status
firefox50 --- fixed

People

(Reporter: rillian, Assigned: rillian)

References

Details

Attachments

(8 files, 13 obsolete files)

103.15 KB, video/mp4
Details
58 bytes, text/x-review-board-request
jya
: review+
Details
58 bytes, text/x-review-board-request
kinetik
: review+
Details
2.41 KB, patch
rillian
: review+
Details | Diff | Splinter Review
22.08 KB, patch
rillian
: review+
Details | Diff | Splinter Review
6.28 KB, patch
rillian
: review+
Details | Diff | Splinter Review
129.08 KB, patch
rillian
: review+
Details | Diff | Splinter Review
666 bytes, patch
kinetik
: review+
Details | Diff | Splinter Review
This bug is about landing the plumbing to let us use the rust mp4parse code when demuxing opus files. - Prefer the opus demuxer when there is an opus track in mp4 - Return the necessary AudioInfo struct from GetTrackInfo()
Assignee: nobody → giles
Anthony, I rebased by work from a few weeks ago. Gotta go for the evening, but if you want to take a look, you can start with this or pull the mp4-opus git branch from https://notabug.org/rillian/firefox
Flags: needinfo?(ajones)
Ralph - do you have Opus/MP4 samples I can try?
I've been using https://people.mozilla.org/~rgiles/2016/sample.mp4 which Matthew made some time ago. He recently offered https://flim.org/~kinetik/jaguar_opus.mp4 as well. It wasn't confirmed correct, though and while the rust demuxer finds an opus track some of the parameters are wonky.
I got 0 audio channels from https://flim.org/~kinetik/jaguar_opus.mp4
Flags: needinfo?(ajones)
Added a simple bit to copy the metadata from the demux and it gets as far as starting to decode now. Next is to hook up the opus decoder.
Attachment #8745799 - Attachment is obsolete: true
Attached patch Document MP4AudioInfo IsValid (obsolete) — Splinter Review
Attachment #8756695 - Flags: review?(jyavenard)
Depends on: 1275812
Attachment #8756695 - Flags: review?(jyavenard) → review+
Rebase on top of bug 1274913.
Attachment #8756691 - Attachment is obsolete: true
The decoder initialization failure turned out to be because we weren't passing the codec_specific data with the Opus header data. We need to re-serialize this in RFC 7845 format, construct and prepend a codecDelay value (pre-skip converted to microseconds in a uint64_t) since the mp4 mapping doesn't store this redundantly like webm, and then pass that in the AudioInfo::mCodecSpecificConfig for the decoder to re-parse and validate. With this change the attached sample.mp4 sample file plays. Thanks to k17e, kinetik, and jya for help tracking down the issue.
Attachment #8762890 - Flags: review?(kinetik)
Attachment #8762891 - Flags: review?(kinetik)
Attachment #8762892 - Flags: review?(kinetik)
Attachment #8762893 - Flags: review?(kinetik)
Attachment #8762894 - Flags: review?(jyavenard)
Attachment #8762896 - Flags: review?(kinetik)
Hook up the GetTrackInfo method to the rust demuxer results. Fill in audio and video track metadata. Prefer rust if it finds more tracks, and accept opus in mp4. Pass audio codec_specific_config to the decoder. With this change sample.mp4 plays. Review commit: https://reviewboard.mozilla.org/r/59340/diff/#index_header See other reviews: https://reviewboard.mozilla.org/r/59340/
Backport serialization changes to the in-tree mp4parse. Provide a convenience function to re-serialize the Opus audio codec-specific config data for clients which need to pass this on to a decoder. Gecko wants this info in ogg/webm format, which means we need to prepend the 'OpusHead' tag and endian-swap the multi-byte fields relative to mp4. Review commit: https://reviewboard.mozilla.org/r/59342/diff/#index_header See other reviews: https://reviewboard.mozilla.org/r/59342/
Save the serialization of the opus header in a HashMap under the track index as part of the context. This needs to be per-track and outlive the call to mp4parse_get_track_audio_info() so this seems the safest place. In mp4parse 0.3.x we can move this to the capi's wrapper struct instead of cluttering up the rust api. Then copy this into an extra field in the audio_info struct so it's available to the caller. Review commit: https://reviewboard.mozilla.org/r/59344/diff/#index_header See other reviews: https://reviewboard.mozilla.org/r/59344/
Jean-Yves and I both found this logic confusing. Review commit: https://reviewboard.mozilla.org/r/59346/diff/#index_header See other reviews: https://reviewboard.mozilla.org/r/59346/
Attachment #8762894 - Flags: review?(jyavenard) → review+
Attachment #8756695 - Attachment is obsolete: true
Comment on attachment 8762890 [details] Bug 1267887 - Experiment with exporting track mime-type. https://reviewboard.mozilla.org/r/59338/#review56346 ::: media/libstagefright/binding/mp4parse/capi.rs:187 (Diff revision 1) > }; > > + info.codec = match &*context.tracks[track_index].mime_type { > + "audio/opus" => TRACK_CODEC_OPUS, > + "video/vp9" => TRACK_CODEC_VP9, > + "video/h264" => TRACK_CODEC_H264, This is "video/avc" in read_video_desc. Probably better to turn them in to constants so we can share the definition. ::: media/libstagefright/binding/mp4parse/capi.rs:188 (Diff revision 1) > > + info.codec = match &*context.tracks[track_index].mime_type { > + "audio/opus" => TRACK_CODEC_OPUS, > + "video/vp9" => TRACK_CODEC_VP9, > + "video/h264" => TRACK_CODEC_H264, > + "audio/aac" => TRACK_CODEC_AAC, And this is audio/mp4a-latm
Attachment #8762890 - Flags: review?(kinetik)
Attachment #8745798 - Attachment is obsolete: true
Attachment #8758062 - Attachment is obsolete: true
Attachment #8762896 - Flags: review?(kinetik) → review+
Comment on attachment 8762893 [details] Bug 1267887 - Export Opus header through the capi. https://reviewboard.mozilla.org/r/59344/#review56356 ::: media/libstagefright/binding/include/mp4parse.h:34 (Diff revision 1) > #define MP4PARSE_TRACK_CODEC_AAC 1 > #define MP4PARSE_TRACK_CODEC_OPUS 2 > #define MP4PARSE_TRACK_CODEC_H264 3 > #define MP4PARSE_TRACK_CODEC_VP9 4 > > +struct CodecSpecific { mp4parse_codec_specific_config or something ::: media/libstagefright/binding/mp4parse/capi.rs:228 (Diff revision 1) > > MP4PARSE_OK > } > > #[no_mangle] > -pub unsafe extern "C" fn mp4parse_get_track_audio_info(context: *mut MediaContext, track: u32, info: *mut TrackAudioInfo) -> i32 { > +pub unsafe extern "C" fn mp4parse_get_track_audio_info(context: *mut MediaContext, track_index: u32, info: *mut TrackAudioInfo) -> i32 { I guess change track to track_index everywhere to keep things consistent. ::: media/libstagefright/binding/mp4parse/capi.rs:275 (Diff revision 1) > - println!("{:?}", opus); > - (*info).codec_specific_config.length = 0; > - (*info).codec_specific_config.data = std::ptr::null(); > + match serialize_opus_header(opus, &mut v) { > + Err(_) => { > + return MP4PARSE_ERROR_INVALID; > + } > + Ok(_) => { > + context.opus_header.insert(track_index, v); Indentation looks off here
Attachment #8762893 - Flags: review?(kinetik) → review+
Comment on attachment 8762892 [details] Bug 1267887 - Add serialization call to make an Opus header. https://reviewboard.mozilla.org/r/59342/#review56380 ::: media/libstagefright/binding/mp4parse/lib.rs:1045 (Diff revision 1) > +/// > +/// Some decoders expect the initialization data in the format used by the > +/// Ogg and WebM encapsulations. To support this we prepend the 'OpusHead' > +/// tag and byte-swap the data from big- to little-endian relative to the > +/// dOps box. > +fn serialize_opus_header<W: byteorder::WriteBytesExt + std::io::Write>(opus: &OpusSpecificBox, dst: &mut W) -> Result<()> { Since this isn't related to MP4 at all, it might be better to put it directly in capi.rs. ::: media/libstagefright/binding/mp4parse/lib.rs:1054 (Diff revision 1) > + if bytes != 8 { > + return Err(Error::InvalidData); > + } > + } > + } > + try!(dst.write_u8(opus.version)); I think you should write version == 1 here, as this value is the version of the OpusHead structure. The value in opus.version is the version of dOps, a similar but unrelated structure with parallel versioning.
Attachment #8762892 - Flags: review?(kinetik)
Comment on attachment 8762891 [details] Bug 1267887 - Support Opus in mp4 with the rust demuxer. https://reviewboard.mozilla.org/r/59340/#review56350 ::: media/libstagefright/binding/MP4Metadata.cpp:197 (Diff revision 1) > } > > mReportedTelemetry = true; > } > + > + if (numTracksRust > numTracks) { I'd prefer it if we detected VPx/Opus more explicitly and preferred mp4parse-rust in that case only. ::: media/libstagefright/binding/MP4Metadata.cpp:544 (Diff revision 1) > uint32_t tracks = mp4parse_get_track_count(mRustState.get()); > MOZ_LOG(sLog, LogLevel::Info, ("rust parser found %u tracks", tracks)); > > uint32_t total = 0; > for (uint32_t i = 0; i < tracks; ++i) { > - mp4parse_track_info track_info; > + struct mp4parse_track_info track_info; None of the mp4parse_ structs should need to be prefixed with structs, since they're typedefs. ::: media/libstagefright/binding/mp4parse/capi.rs:193 (Diff revision 1) > TrackType::Unknown => return MP4PARSE_ERROR_UNSUPPORTED, > }; > > info.codec = match &*context.tracks[track_index].mime_type { > "audio/opus" => TRACK_CODEC_OPUS, > + "audio/aac" | We're not producing audio/aac or video/h264 as MIME strings anywhere in the parser, so I don't understand why we'd accept them here.
Attachment #8762891 - Flags: review?(kinetik)
Carrying r=kinetik from bug 1275812.
Attachment #8762890 - Attachment is obsolete: true
Attachment #8765643 - Flags: review+
Attached patch Support Opus in mp4 v2 (obsolete) — Splinter Review
Rebase on top of mp4parse v0.4.0, incorporating review comments.
Attachment #8762891 - Attachment is obsolete: true
Attachment #8765644 - Flags: review?(kinetik)
Attachment #8765647 - Flags: review?(kinetik)
Attached patch Result of running update script (obsolete) — Splinter Review
Attachment #8765649 - Flags: review?(kinetik)
Attachment #8762892 - Attachment is obsolete: true
Attachment #8762893 - Attachment is obsolete: true
Comment on attachment 8765644 [details] [diff] [review] Support Opus in mp4 v2 Review of attachment 8765644 [details] [diff] [review]: ----------------------------------------------------------------- ::: media/libstagefright/binding/DecoderData.cpp @@ +187,5 @@ > } > > +#ifdef MOZ_RUST_MP4PARSE > +void > +MP4VideoInfo::Update(const struct mp4parse_track_info* track, These don't need struct on them, even in pure C, because they're typedefs. @@ +188,5 @@ > > +#ifdef MOZ_RUST_MP4PARSE > +void > +MP4VideoInfo::Update(const struct mp4parse_track_info* track, > + const struct mp4parse_track_video_info* video) Ditto. ::: media/libstagefright/binding/MP4Metadata.cpp @@ +117,5 @@ > + > + bool Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read); > + > +private: > + Stream *mSource; Stream* mSource; @@ +142,5 @@ > private: > CryptoFile mCrypto; > RefPtr<Stream> mSource; > + RustStreamAdaptor mRustSource; > + const mp4parse_io mRustIo; mp4parse_new copies the mp4parse_io contents, so there's no need to keep one around on this side and the code is clearer if we don't. @@ +217,5 @@ > mReportedTelemetry = true; > } > + > + if (mPreferRust || ShouldPreferRust()) { > + MOZ_LOG(sLog, LogLevel::Info, ("Prefering rust demuxer")); Preferring @@ +443,4 @@ > if (e && e->IsValid()) { > return e; > } > + Whitespace? @@ +597,5 @@ > MOZ_LOG(sLog, LogLevel::Info, ("rust parser found %u tracks", tracks)); > > uint32_t total = 0; > for (uint32_t i = 0; i < tracks; ++i) { > + struct mp4parse_track_info track_info; Doesn't need struct, it's a typedef. @@ +627,5 @@ > size_t aTrackNumber) const > { > + static LazyLogModule sLog("MP4Metadata"); > + > + struct mp4parse_track_info info; No struct @@ +650,5 @@ > + // This specialization interface is crazy. > + UniquePtr<mozilla::TrackInfo> e; > + switch (aType) { > + case TrackInfo::TrackType::kAudioTrack: { > + struct mp4parse_track_audio_info audio; No struct @@ +663,5 @@ > + // The Opus decoder expects the container's codec delay or > + // pre-skip value, in microseconds, as a 64-bit int at the > + // start of the codec-specific config blob. > + MOZ_ASSERT(audio.codec_specific_config.data); > + MOZ_ASSERT(audio.codec_specific_config.length >= 12); The Rust code included in *this* patch is setting this to nullptr/0, so doesn't this assert always trigger? @@ +671,5 @@ > + ("Copying opus pre-skip value of %d as CodecDelay.",(int)preskip)); > + uint8_t codecDelay[sizeof(uint64_t)]; > + BigEndian::writeUint64(codecDelay, > + mozilla::FramesToUsecs(preskip, 48000).value()); > + track->mCodecSpecificConfig->AppendElements(codecDelay, sizeof(uint64_t)); This chunk should be abstracted out so we can share it with the WebM demuxer. @@ +687,5 @@ > + e = Move(track); > + } > + break; > + case TrackInfo::TrackType::kVideoTrack: { > + struct mp4parse_track_video_info video; No struct ::: media/libstagefright/binding/include/mp4_demuxer/DecoderData.h @@ +22,5 @@ > +#ifdef MOZ_RUST_MP4PARSE > +extern "C" { > +struct mp4parse_track_info; > +struct mp4parse_track_audio_info; > +struct mp4parse_track_video_info; Technically these should be: typedef struct foo foo; to match the mp4parse.h header. @@ +81,5 @@ > const char* aMimeType); > > +#ifdef MOZ_RUST_MP4PARSE > + void Update(const struct mp4parse_track_info* track, > + const struct mp4parse_track_video_info* video); Should need struct on these either. ::: media/libstagefright/binding/mp4parse/capi.rs @@ +195,3 @@ > "video/vp9" => TRACK_CODEC_VP9, > + "video/h264" | > + "video/avc" => TRACK_CODEC_H264, Remove video/h264 and audio/aac, because the parser never produces these strings so it doesn't make sense to match on them. ::: media/libstagefright/gtest/TestMP4Rust.cpp @@ +113,4 @@ > mp4parse_free(context); > } > > + Whitespace? It's a bit of a shame we have to duplicate this testing in multiple places; this looks quite similar to what we're testing in the upstream repo already.
Attachment #8765644 - Flags: review?(kinetik)
Attachment #8765647 - Flags: review?(kinetik) → review+
Comment on attachment 8765649 [details] [diff] [review] Result of running update script Review of attachment 8765649 [details] [diff] [review]: ----------------------------------------------------------------- ::: media/libstagefright/binding/opus-header.patch @@ +1,1 @@ > +diff --git a/media/libstagefright/binding/mp4parse/lib.rs b/media/libstagefright/binding/mp4parse/lib.rs Both of the opus-header patches are obsolete and should be removed, right? The code is upstream now.
Attachment #8765649 - Flags: review?(kinetik)
(In reply to Matthew Gregan [:kinetik] from comment #30) > Both of the opus-header patches are obsolete and should be removed, right? > The code is upstream now. That's right. Looks like a rebase oversight.
Update patch addressing review comments. Carrying r=kinetik. (In reply to Matthew Gregan [:kinetik] from comment #29) > Comment on attachment 8765644 [details] [diff] [review] > Support Opus in mp4 v2 > [...] > > +MP4VideoInfo::Update(const struct mp4parse_track_info* track, > > These don't need struct on them, even in pure C, because they're typedefs. Ok, removed. > mp4parse_new copies the mp4parse_io contents, so there's no need to keep one > around on this side and the code is clearer if we don't. I did think because I couldn't figure out a nice way to initialize the UniquePtr in the ctor body. Replaced with `mRustParser.reset(mp4parse_new(...));` after irc discussion. > @@ +663,5 @@ > > + // The Opus decoder expects the container's codec delay or > > + // pre-skip value, in microseconds, as a 64-bit int at the > > + // start of the codec-specific config blob. > > + MOZ_ASSERT(audio.codec_specific_config.data); > > + MOZ_ASSERT(audio.codec_specific_config.length >= 12); > > The Rust code included in *this* patch is setting this to nullptr/0, so > doesn't this assert always trigger? The rust code included in the previous version of the patch was a rebase error. Should be better now. > > @@ +671,5 @@ > > + ("Copying opus pre-skip value of %d as CodecDelay.",(int)preskip)); > > + uint8_t codecDelay[sizeof(uint64_t)]; > > + BigEndian::writeUint64(codecDelay, > > + mozilla::FramesToUsecs(preskip, 48000).value()); > > + track->mCodecSpecificConfig->AppendElements(codecDelay, sizeof(uint64_t)); > > This chunk should be abstracted out so we can share it with the WebM demuxer. I'd like to do that in a follow up bug. > ::: media/libstagefright/binding/include/mp4_demuxer/DecoderData.h > @@ +22,5 @@ > > +#ifdef MOZ_RUST_MP4PARSE > > +extern "C" { > > +struct mp4parse_track_info; > > +struct mp4parse_track_audio_info; > > +struct mp4parse_track_video_info; > > Technically these should be: typedef struct foo foo; to match the mp4parse.h > header. Ok, updated. They seem to work without even after removing all the 'struct' keywords above. Perhaps that's just because of the unified build. > ::: media/libstagefright/binding/mp4parse/capi.rs > @@ +195,3 @@ > > "video/vp9" => TRACK_CODEC_VP9, > > + "video/h264" | > > + "video/avc" => TRACK_CODEC_H264, > > Remove video/h264 and audio/aac, because the parser never produces these > strings so it doesn't make sense to match on them. Spurious. I've removed the whole hunk. Sorry about that. > ::: media/libstagefright/gtest/TestMP4Rust.cpp > [...] > > It's a bit of a shame we have to duplicate this testing in multiple places; > this looks quite similar to what we're testing in the upstream repo already. I agree, but I think it's important to have something in-tree as long as we have separate change-control for the two repos. Maybe once we get cargo integration we can have `cargo test` in the gecko tier-1 jobs and then we can drop the gtest.
Attachment #8765644 - Attachment is obsolete: true
Attachment #8766083 - Flags: review+
Clean up issues with update script patch. No functional changes. Carrying forward r=kinetik.
Attachment #8765647 - Attachment is obsolete: true
Attachment #8766084 - Flags: review+
Rerun import script to get a clean patch after the other changes. Carrying forward r=kinetik.
Attachment #8765649 - Attachment is obsolete: true
Attachment #8766087 - Flags: review+
Blocks: 1282963
Forgot to add boxes.rs when redoing the patch.
Attachment #8766087 - Attachment is obsolete: true
Attachment #8766116 - Flags: review+
One last fix from try: MSVC apparently needs an include for std::min in TestMP4Rust.cpp. With this change it looks ready to land: https://treeherder.mozilla.org/#/jobs?repo=try&revision=5cda3e0f1478 (windows build broken without this patch) https://treeherder.mozilla.org/#/jobs?repo=try&revision=5153e9163f34&selectedJob=23074431 (windows build fixed with this patch)
Attachment #8766165 - Flags: review?(kinetik)
Attachment #8766165 - Flags: review?(kinetik) → review+
Pushed by rgiles@mozilla.com: https://hg.mozilla.org/integration/mozilla-inbound/rev/e19c4c4eac51 Experiment with exporting track mime-type. r=kinetik https://hg.mozilla.org/integration/mozilla-inbound/rev/72ea61f4f820 Support Opus in mp4 with the rust demuxer. r=kinetik https://hg.mozilla.org/integration/mozilla-inbound/rev/3b1177fd30cb Update script for mp4parse v0.4.0. r=kinetik https://hg.mozilla.org/integration/mozilla-inbound/rev/c697af8c555a Document MP4AudioInfo::IsValid(). r=jya https://hg.mozilla.org/integration/mozilla-inbound/rev/9128f4f94032 Log short opus codec config blocks. r=kinetik https://hg.mozilla.org/integration/mozilla-inbound/rev/e7e34b315a0a Add algorithm.h for std::min on msvc. r=kinetik
Yeouch. Backed out in https://hg.mozilla.org/integration/mozilla-inbound/rev/348930b0d9db48ced9ec44b5b499517b8e828906 for crashing (without, of course, a stack) on every Win8 suite which involves Marionette behind the scenes, with a useless stack in the actual Marionette suite, and failing or crashing in gtests.
Depends on: 1305250
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: