RESOLVED FIXED 227110
[WebAudio] Add webm/opus container support
https://bugs.webkit.org/show_bug.cgi?id=227110
Summary [WebAudio] Add webm/opus container support
Jean-Yves Avenard [:jya]
Reported 2021-06-17 00:36:39 PDT
WebAudio currently only works with content AudioToolBox/CoreMedia supports; and it doesn't support webm.
Attachments
Patch (131.25 KB, patch)
2021-07-21 20:20 PDT, Jean-Yves Avenard [:jya]
ews-feeder: commit-queue-
Patch (90.77 KB, patch)
2021-07-21 21:33 PDT, Jean-Yves Avenard [:jya]
ews-feeder: commit-queue-
Patch (90.77 KB, patch)
2021-07-21 21:42 PDT, Jean-Yves Avenard [:jya]
ews-feeder: commit-queue-
Patch (90.84 KB, patch)
2021-07-21 22:07 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (90.84 KB, patch)
2021-07-21 22:10 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (91.10 KB, patch)
2021-07-21 23:18 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (131.95 KB, patch)
2021-07-22 00:31 PDT, Jean-Yves Avenard [:jya]
ews-feeder: commit-queue-
Patch (132.00 KB, patch)
2021-07-22 03:49 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (132.09 KB, patch)
2021-07-22 04:00 PDT, Jean-Yves Avenard [:jya]
ews-feeder: commit-queue-
Patch (132.09 KB, patch)
2021-07-22 04:49 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (131.42 KB, patch)
2021-07-22 05:48 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (132.71 KB, patch)
2021-07-22 17:27 PDT, Jean-Yves Avenard [:jya]
ews-feeder: commit-queue-
Patch (119.29 KB, patch)
2021-07-22 21:09 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (121.17 KB, patch)
2021-07-25 17:17 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (121.17 KB, patch)
2021-07-25 18:12 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (121.37 KB, patch)
2021-07-25 18:59 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (64.43 KB, patch)
2021-07-26 00:57 PDT, Jean-Yves Avenard [:jya]
ews-feeder: commit-queue-
Patch (64.49 KB, patch)
2021-07-26 01:22 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (120.76 KB, patch)
2021-07-26 02:51 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (120.88 KB, patch)
2021-07-28 18:50 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (120.88 KB, patch)
2021-07-28 18:53 PDT, Jean-Yves Avenard [:jya]
no flags
Patch (120.86 KB, patch)
2021-07-28 20:26 PDT, Jean-Yves Avenard [:jya]
no flags
Radar WebKit Bug Importer
Comment 1 2021-06-17 00:38:14 PDT
Jean-Yves Avenard [:jya]
Comment 2 2021-07-21 00:21:24 PDT
Will do opus first, Vorbis doesn't work for multiple reasons and will be handled in a different bug
Jean-Yves Avenard [:jya]
Comment 3 2021-07-21 20:20:15 PDT
Jean-Yves Avenard [:jya]
Comment 4 2021-07-21 21:33:35 PDT
Created attachment 433988 [details] Patch rebase; fix compilation on tvOS and watchOS fix linker error when < BigSur
Jean-Yves Avenard [:jya]
Comment 5 2021-07-21 21:42:45 PDT
Created attachment 433989 [details] Patch duh, fix compilation on tvOS and watchOS #2
Jean-Yves Avenard [:jya]
Comment 6 2021-07-21 22:07:37 PDT
Created attachment 433990 [details] Patch Make WebM code conditional on MEDIA_SOURCE as SourceBufferParserWebM is only included with that
Jean-Yves Avenard [:jya]
Comment 7 2021-07-21 22:10:13 PDT
Created attachment 433991 [details] Patch Make WebM code conditional on MEDIA_SOURCE as SourceBufferParserWebM is only included with that
Jean-Yves Avenard [:jya]
Comment 8 2021-07-21 23:18:01 PDT
Created attachment 433993 [details] Patch Fix compilation #4
Jean-Yves Avenard [:jya]
Comment 9 2021-07-22 00:31:54 PDT
Created attachment 433995 [details] Patch webm sample files got dropped during rebase :(
Jean-Yves Avenard [:jya]
Comment 10 2021-07-22 03:49:28 PDT
Created attachment 434003 [details] Patch fix vorbis decoding error (was missing magic cookie), avoid intermediate buffer
Jean-Yves Avenard [:jya]
Comment 11 2021-07-22 04:00:53 PDT
Created attachment 434005 [details] Patch limit number of frames to be decoded at once to a sane limit as CM only deals with uint32_t sizes
Alicia Boya García
Comment 12 2021-07-22 04:18:04 PDT
Comment on attachment 434005 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=434005&action=review > Source/WebCore/platform/MediaSample.h:129 > + virtual bool isMediaSampleAVFObjC() const { return false; } Why is this necessary? Is there a second specialization of MediaSample for the Apple ports that I'm missing?
Jean-Yves Avenard [:jya]
Comment 13 2021-07-22 04:49:07 PDT
Created attachment 434006 [details] Patch limit number of frames to be decoded at once to a sane limit as CM only deals with uint32_t sizes
Jean-Yves Avenard [:jya]
Comment 14 2021-07-22 04:52:47 PDT
(In reply to Alicia Boya García from comment #12) > Comment on attachment 434005 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=434005&action=review > > > Source/WebCore/platform/MediaSample.h:129 > > + virtual bool isMediaSampleAVFObjC() const { return false; } > > Why is this necessary? Is there a second specialization of MediaSample for > the Apple ports that I'm missing? On Mac no, but the code does get a MediaSample; seems nicer to me that way than just assuming and using static_cast.
Jean-Yves Avenard [:jya]
Comment 15 2021-07-22 05:48:05 PDT
Created attachment 434007 [details] Patch Apply comment, can just do static_cast to MediaSampleAVFObjC safely
Jean-Yves Avenard [:jya]
Comment 16 2021-07-22 17:27:32 PDT
Created attachment 434046 [details] Patch hopefully fix compilation on windows
Jean-Yves Avenard [:jya]
Comment 17 2021-07-22 21:09:20 PDT
Created attachment 434060 [details] Patch Window compilation fix #2. Manually edit the FeatureFlags plist to reduce diff size
Eric Carlson
Comment 18 2021-07-23 04:46:51 PDT
Comment on attachment 434060 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=434060&action=review This looks OK to me, but it'd be good to have another set of eyes on it. > Source/WTF/ChangeLog:9 > + Add webm_webaudio preference to make canPlayType return probably for mimetype: `webm_webaudio` is the system feature flag and the preference is `WebMWebAudioEnabled` so you probably mean the later? > Source/WebKit/ChangeLog:9 > + Add default value for webm_webaudio pref for the different platforms. s/pref/feature flag/ > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:187 > +#if ENABLE(MEDIA_SOURCE) > + if (isMaybeWebM(static_cast<const uint8_t*>(data), dataSize)) { > + auto parser = adoptRef(new SourceBufferParserWebM()); > + bool error = false; > + std::optional<uint64_t> audioTrackId; > + MediaTime duration; > + SourceBufferParserWebM::InitializationSegment initSegment; > + Vector<Ref<MediaSampleAVFObjC>> samples; > + parser->setDidEncounterErrorDuringParsingCallback([&](uint64_t) { > + error = true; > + }); > + parser->setDidParseInitializationDataCallback([&](SourceBufferParserWebM::InitializationSegment&& init) { > + for (auto& audioTrack : init.audioTracks) { > + if (audioTrack.track && audioTrack.track->trackUID()) { > + duration = init.duration; > + audioTrackId = audioTrack.track->trackUID(); > + initSegment = WTFMove(init); > + return; > + } > + } > + }); > + parser->setDidProvideMediaDataCallback([&](Ref<MediaSample>&& sample, uint64_t trackID, const String&) { > + if (!audioTrackId || trackID != *audioTrackId) > + return; > + samples.append(static_reference_cast<MediaSampleAVFObjC>(WTFMove(sample))); > + }); > + parser->setCallOnClientThreadCallback([](WTF::Function<void()>&& function) { > + function(); > + }); > + Vector<uint8_t> vector(static_cast<const uint8_t*>(data), dataSize); > + SourceBufferParser::Segment segment(WTFMove(vector)); > + parser->appendData(WTFMove(segment)); > + if (audioTrackId) { > + parser->flushPendingAudioBuffers(); > + m_webmData = makeUnique<AudioFileReaderWebMData>(AudioFileReaderWebMData { WTFMove(initSegment), WTFMove(duration), WTFMove(samples) }); > + } > + return; > + } > +#endif Nit: this is big enough that I would put it into a separate method. > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:253 > +static OSStatus passthroughInputDataCallback( > + AudioConverterRef, UInt32* numDataPackets, AudioBufferList* data, AudioStreamPacketDescription** packetDesc, void* inUserData) Nit: WebKit style is to have the parameters on the same line as the function. > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:283 > + if (m_webmData) { Nit: flip this logic and it can be an early return. > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:298 > + if (!asbd) > + return -1; Is it worth RELEASE_LOG_FAULT-ing this unexpected failures too? > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:302 > + if (PAL::CMSampleBufferGetAudioStreamPacketDescriptions(sampleBuffer, 0, nullptr, &packetDescriptionsSize) != noErr) > + return -1; Ditto. > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:305 > + if (!numDescriptions) > + return -1; Ditto. > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:308 > + if (PAL::CMSampleBufferGetAudioStreamPacketDescriptions(sampleBuffer, packetDescriptionsSize, descriptions.get(), nullptr) != noErr) > + return -1; Ditto. > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:443 > + AudioConverterRef converter; > + if (PAL::AudioConverterNew(&*inFormat, &outFormat, &converter) != noErr) > + return nullptr; > + auto cleanup = makeScopeExit([&] { > + PAL::AudioConverterDispose(converter); > + }); > + CMFormatDescriptionRef formatDescription = PAL::CMSampleBufferGetFormatDescription(m_webmData->m_samples[0]->sampleBuffer()); Nit: this is big enough that I would put it in a separate method.
Peng Liu
Comment 19 2021-07-23 10:36:11 PDT
Comment on attachment 434060 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=434060&action=review > Source/WebCore/platform/audio/AudioBus.h:87 > + void setLength(size_t newLength); Nit. The parameter name can be omitted. > Source/WebCore/platform/audio/AudioChannel.h:77 > + // Set new length. Can only be set to a value lower than the currently Nit. s/currently set length/current length/ > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:121 > + AudioBufferList* operator*() const { return m_bufferList; } Return a reference here? > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:128 > +static constexpr uint32_t kVorbisMaxFrameLength = 4096; Nit. Maybe a reference or comment regarding the constants? > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:255 > + PassthroughUserData* userData = (PassthroughUserData*)inUserData; Can we use `auto *` here? > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:292 > + CMSampleBufferRef sampleBuffer = sample->sampleBuffer(); Use auto? > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:293 > + const CMFormatDescriptionRef formatDescription = PAL::CMSampleBufferGetFormatDescription(sampleBuffer); Ditto. > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:309 > + CMItemCount numPackets = PAL::CMSampleBufferGetNumSamples(sampleBuffer); Use auto? > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:357 > + AudioStreamBasicDescription outFormat = inFormat; Use auto? > Source/WebCore/platform/graphics/avfoundation/objc/MediaSampleAVFObjC.h:47 > + WEBCORE_EXPORT RefPtr<JSC::Uint8ClampedArray> getRGBAImageData() const override; Nit. Not a problem of this patch. I am wondering whether we can replace all these "override" with "final" here. > Source/WebKit/FeatureFlags/WebKit-watchos.plist:93 > + <true/> Will we enable the feature for all Cocoa ports?
Jean-Yves Avenard [:jya]
Comment 20 2021-07-25 07:02:54 PDT
Comment on attachment 434060 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=434060&action=review >> Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:121 >> + AudioBufferList* operator*() const { return m_bufferList; } > > Return a reference here? not in the context in which it is used. AudioBufferListHolder is a wrapper of AudioBufferList* not an AudioBufferList I guess I could rename the class AudioBufferListPtrHolder to make it clearer. >> Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:128 >> +static constexpr uint32_t kVorbisMaxFrameLength = 4096; > > Nit. Maybe a reference or comment regarding the constants? actually, those are no longer used; will remove >> Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:357 >> + AudioStreamBasicDescription outFormat = inFormat; > > Use auto? I find it makes the code less readable, particularly as we are modifying the internal structure of the object rather than using it as a temporary object that we just carried around. So prefer to leave it as-is >> Source/WebKit/FeatureFlags/WebKit-watchos.plist:93 >> + <true/> > > Will we enable the feature for all Cocoa ports? wherever WebM is supported I guess. in any case wherever the webm parser is enabled (as this feature flag follows the same logic)
Jean-Yves Avenard [:jya]
Comment 21 2021-07-25 17:17:50 PDT
Created attachment 434186 [details] Patch Apply comments
Jean-Yves Avenard [:jya]
Comment 22 2021-07-25 18:12:21 PDT
Created attachment 434187 [details] Patch fix tvos compilation
Jean-Yves Avenard [:jya]
Comment 23 2021-07-25 18:59:13 PDT
Created attachment 434189 [details] Patch fix tvos compilation, with the right patch it works better
Jean-Yves Avenard [:jya]
Comment 24 2021-07-26 00:57:07 PDT
Created attachment 434193 [details] Patch Reduce size of patch a bit, make methods const
Jean-Yves Avenard [:jya]
Comment 25 2021-07-26 01:22:11 PDT
Created attachment 434195 [details] Patch tvos again
Jean-Yves Avenard [:jya]
Comment 26 2021-07-26 02:51:50 PDT
Created attachment 434196 [details] Patch opus.webm and vorbis.webm got dropped somehow from patch
Jer Noble
Comment 27 2021-07-27 16:28:23 PDT
Comment on attachment 434196 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=434196&action=review Overall comments, I wonder if we should subclass AudioFileReader to AudioFileReaderWebM or something, rather than putting two implementations in the same class. > Source/WebCore/html/HTMLMediaElement.cpp:8186 > +bool HTMLMediaElement::webMWebAudioEnabled() const > +{ > +#if ENABLE(MEDIA_SOURCE) > + return RuntimeEnabledFeatures::sharedFeatures().webMWebAudioEnabled(); > +#else > + return false; > +#endif > +} > + Nit: this can just be a static method; no need to add it to the class header. > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:206 > + parser->setCallOnClientThreadCallback([](WTF::Function<void()>&& function) { Nit: could just use `auto&& function` here. > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:210 > + Vector<uint8_t> vector(data, dataSize); > + SourceBufferParser::Segment segment(WTFMove(vector)); Nit: could this just be: `SourceBufferParser::Segment segment({data, dataSize})`? Or is type deduction not smart enough here? > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:221 > + UInt32 m_dataSize; > + const void* m_data; Could PassthroughUserData just take a CMSampleBufferRef, rather than a `void*` and a data size? > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:232 > +static OSStatus passthroughInputDataCallback(AudioConverterRef, UInt32* numDataPackets, AudioBufferList* data, AudioStreamPacketDescription** packetDesc, void* inUserData) > +{ > + auto* userData = static_cast<PassthroughUserData*>(inUserData); Out of an abundance of caution, we should null-check all the inputs to this method, with ASSERT() macros to catch null inputs in tests. > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:257 > +Vector<AudioStreamPacketDescription> AudioFileReader::getPacketDescriptions(CMSampleBufferRef sampleBuffer) const > +{ Ditto the comment about null-checking. (Not sure if the CM* methods crash or throw if fed a null CMSampleBufferRef.) > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:282 > +std::optional<size_t> AudioFileReader::decodeWebMData(AudioBufferList* bufferList, size_t numberOfFrames, const AudioStreamBasicDescription& inFormat, const AudioStreamBasicDescription& outFormat) const > +{ Ditto null-checking (or make AudioBufferList a &-ref). > Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:291 > + auto formatDescription = PAL::CMSampleBufferGetFormatDescription(m_webmData->m_samples[0]->sampleBuffer()); Ditto here. /more to come/
Jean-Yves Avenard [:jya]
Comment 28 2021-07-27 16:39:01 PDT
Comment on attachment 434196 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=434196&action=review >> Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:221 >> + const void* m_data; > > Could PassthroughUserData just take a CMSampleBufferRef, rather than a `void*` and a data size? but then you have to re-run the code to extract the data and size every single sub-packets. Seems rather inefficient when we could just do it once. >> Source/WebCore/platform/audio/cocoa/AudioFileReaderCocoa.cpp:282 >> +{ > > Ditto null-checking (or make AudioBufferList a &-ref). this is a private method, only called internally. AudioBufferList can't ever be null as it is always checked at the point of creation. I can add an assert for clarity. But it can never be null.
Jean-Yves Avenard [:jya]
Comment 29 2021-07-28 18:50:03 PDT
Created attachment 434485 [details] Patch Apply comments
Jean-Yves Avenard [:jya]
Comment 30 2021-07-28 18:53:08 PDT
Created attachment 434487 [details] Patch Apply comments
EWS
Comment 31 2021-07-28 20:23:03 PDT
ChangeLog entry in LayoutTests/ChangeLog contains OOPS!.
Jean-Yves Avenard [:jya]
Comment 32 2021-07-28 20:26:50 PDT
Created attachment 434493 [details] Patch Fix changelog
EWS
Comment 33 2021-07-28 21:18:23 PDT
Committed r280416 (240056@main): <https://commits.webkit.org/240056@main> All reviewed patches have been landed. Closing bug and clearing flags on attachment 434493 [details].
Note You need to log in before you can comment on or make changes to this bug.