diff options
-rw-r--r-- | dom/media/MediaData.h | 3 | ||||
-rw-r--r-- | dom/media/platforms/agnostic/VPXDecoder.cpp | 53 | ||||
-rw-r--r-- | dom/media/platforms/agnostic/VPXDecoder.h | 12 | ||||
-rw-r--r-- | dom/media/webm/WebMDemuxer.cpp | 145 | ||||
-rw-r--r-- | dom/media/webm/WebMDemuxer.h | 14 |
5 files changed, 152 insertions, 75 deletions
diff --git a/dom/media/MediaData.h b/dom/media/MediaData.h index a79aac6ed..905b4c1d9 100644 --- a/dom/media/MediaData.h +++ b/dom/media/MediaData.h @@ -14,6 +14,7 @@ #include "nsIMemoryReporter.h" #include "SharedBuffer.h" #include "mozilla/RefPtr.h" +#include "mozilla/Span.h" #include "mozilla/UniquePtr.h" #include "mozilla/UniquePtrExtensions.h" #include "nsTArray.h" @@ -631,6 +632,8 @@ public: { return sizeof(*this) + mBuffer.ComputedSizeOfExcludingThis(); } + // Access the buffer as a Span. + operator Span<const uint8_t>() { return MakeSpan(Data(), Size()); } const CryptoSample& mCrypto; RefPtr<MediaByteBuffer> mExtraData; diff --git a/dom/media/platforms/agnostic/VPXDecoder.cpp b/dom/media/platforms/agnostic/VPXDecoder.cpp index 77c81b51b..f2f84487f 100644 --- a/dom/media/platforms/agnostic/VPXDecoder.cpp +++ b/dom/media/platforms/agnostic/VPXDecoder.cpp @@ -22,7 +22,7 @@ namespace mozilla { using namespace gfx; using namespace layers; -static int MimeTypeToCodec(const nsACString& aMimeType) +static VPXDecoder::Codec MimeTypeToCodec(const nsACString& aMimeType) { if (aMimeType.EqualsLiteral("video/webm; codecs=vp8")) { return VPXDecoder::Codec::VP8; @@ -31,7 +31,7 @@ static int MimeTypeToCodec(const nsACString& aMimeType) } else if (aMimeType.EqualsLiteral("video/vp9")) { return VPXDecoder::Codec::VP9; } - return -1; + return VPXDecoder::Codec::Unknown; } VPXDecoder::VPXDecoder(const CreateDecoderParams& aParams) @@ -101,17 +101,10 @@ MediaResult VPXDecoder::DoDecode(MediaRawData* aSample) { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + #if defined(DEBUG) - vpx_codec_stream_info_t si; - PodZero(&si); - si.sz = sizeof(si); - if (mCodec == Codec::VP8) { - vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), aSample->Data(), aSample->Size(), &si); - } else if (mCodec == Codec::VP9) { - vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), aSample->Data(), aSample->Size(), &si); - } - NS_ASSERTION(bool(si.is_kf) == aSample->mKeyframe, - "VPX Decode Keyframe error sample->mKeyframe and si.si_kf out of sync"); + NS_ASSERTION(IsKeyframe(*aSample, mCodec) == aSample->mKeyframe, + "VPX Decode Keyframe error sample->mKeyframe and sample data out of sync"); #endif if (vpx_codec_err_t r = vpx_codec_decode(&mVPX, aSample->Data(), aSample->Size(), nullptr, 0)) { @@ -249,5 +242,41 @@ VPXDecoder::IsVP9(const nsACString& aMimeType) return IsVPX(aMimeType, VPXDecoder::VP9); } +/* static */ +bool +VPXDecoder::IsKeyframe(Span<const uint8_t> aBuffer, Codec aCodec) +{ + vpx_codec_stream_info_t si; + PodZero(&si); + si.sz = sizeof(si); + + if (aCodec == Codec::VP8) { + vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), aBuffer.Elements(), aBuffer.Length(), &si); + return bool(si.is_kf); + } else if (aCodec == Codec::VP9) { + vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), aBuffer.Elements(), aBuffer.Length(), &si); + return bool(si.is_kf); + } + + return false; +} + +/* static */ +nsIntSize +VPXDecoder::GetFrameSize(Span<const uint8_t> aBuffer, Codec aCodec) +{ + vpx_codec_stream_info_t si; + PodZero(&si); + si.sz = sizeof(si); + + if (aCodec == Codec::VP8) { + vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), aBuffer.Elements(), aBuffer.Length(), &si); + } else if (aCodec == Codec::VP9) { + vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), aBuffer.Elements(), aBuffer.Length(), &si); + } + + return nsIntSize(si.w, si.h); +} + } // namespace mozilla #undef LOG diff --git a/dom/media/platforms/agnostic/VPXDecoder.h b/dom/media/platforms/agnostic/VPXDecoder.h index d420ec069..4e8d83617 100644 --- a/dom/media/platforms/agnostic/VPXDecoder.h +++ b/dom/media/platforms/agnostic/VPXDecoder.h @@ -7,6 +7,7 @@ #define VPXDecoder_h_ #include "PlatformDecoderModule.h" +#include "mozilla/Span.h" #include <stdint.h> #define VPX_DONT_DEFINE_STDINT_TYPES @@ -36,7 +37,8 @@ public: enum Codec: uint8_t { VP8 = 1 << 0, - VP9 = 1 << 1 + VP9 = 1 << 1, + Unknown = 1 << 7, }; // Return true if aMimeType is a one of the strings used by our demuxers to @@ -46,6 +48,12 @@ public: static bool IsVP8(const nsACString& aMimeType); static bool IsVP9(const nsACString& aMimeType); + // Return true if a sample is a keyframe for the specified codec. + static bool IsKeyframe(Span<const uint8_t> aBuffer, Codec aCodec); + + // Return the frame dimensions for a sample for the specified codec. + static nsIntSize GetFrameSize(Span<const uint8_t> aBuffer, Codec aCodec); + private: void ProcessDecode(MediaRawData* aSample); MediaResult DoDecode(MediaRawData* aSample); @@ -61,7 +69,7 @@ private: const VideoInfo& mInfo; - const int mCodec; + const Codec mCodec; }; } // namespace mozilla diff --git a/dom/media/webm/WebMDemuxer.cpp b/dom/media/webm/WebMDemuxer.cpp index 20ed71581..b54739c06 100644 --- a/dom/media/webm/WebMDemuxer.cpp +++ b/dom/media/webm/WebMDemuxer.cpp @@ -9,6 +9,7 @@ #include "AbstractMediaDecoder.h" #include "MediaResource.h" #include "OpusDecoder.h" +#include "VPXDecoder.h" #include "WebMDemuxer.h" #include "WebMBufferedParser.h" #include "gfx2DGlue.h" @@ -24,12 +25,9 @@ #include "mozilla/Sprintf.h" #include <algorithm> +#include <numeric> #include <stdint.h> -#define VPX_DONT_DEFINE_STDINT_TYPES -#include "vpx/vp8dx.h" -#include "vpx/vpx_decoder.h" - #define WEBM_DEBUG(arg, ...) MOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, ("WebMDemuxer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) extern mozilla::LazyLogModule gMediaDemuxerLog; @@ -549,7 +547,7 @@ WebMDemuxer::GetTrackCrypto(TrackInfo::TrackType aType, size_t aTrackNumber) { return crypto; } -bool +nsresult WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSamples) { if (mIsMediaSource) { @@ -557,17 +555,18 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl EnsureUpToDateIndex(); } - RefPtr<NesteggPacketHolder> holder(NextPacket(aType)); + RefPtr<NesteggPacketHolder> holder; + nsresult rv = NextPacket(aType, holder); - if (!holder) { - return false; + if (NS_FAILED(rv)) { + return rv; } int r = 0; unsigned int count = 0; r = nestegg_packet_count(holder->Packet(), &count); if (r == -1) { - return false; + return NS_ERROR_DOM_MEDIA_DEMUXER_ERR; } int64_t tstamp = holder->Timestamp(); int64_t duration = holder->Duration(); @@ -578,7 +577,11 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl // video frame. int64_t next_tstamp = INT64_MIN; if (aType == TrackInfo::kAudioTrack) { - RefPtr<NesteggPacketHolder> next_holder(NextPacket(aType)); + RefPtr<NesteggPacketHolder> next_holder; + rv = NextPacket(aType, next_holder); + if (NS_FAILED(rv) && rv != NS_ERROR_DOM_MEDIA_END_OF_STREAM) { + return rv; + } if (next_holder) { next_tstamp = next_holder->Timestamp(); PushAudioPacket(next_holder); @@ -593,7 +596,11 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl } mLastAudioFrameTime = Some(tstamp); } else if (aType == TrackInfo::kVideoTrack) { - RefPtr<NesteggPacketHolder> next_holder(NextPacket(aType)); + RefPtr<NesteggPacketHolder> next_holder; + rv = NextPacket(aType, next_holder); + if (NS_FAILED(rv) && rv != NS_ERROR_DOM_MEDIA_END_OF_STREAM) { + return rv; + } if (next_holder) { next_tstamp = next_holder->Timestamp(); PushVideoPacket(next_holder); @@ -610,7 +617,7 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl } if (mIsMediaSource && next_tstamp == INT64_MIN) { - return false; + return NS_ERROR_DOM_MEDIA_END_OF_STREAM; } int64_t discardPadding = 0; @@ -626,7 +633,7 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl r = nestegg_packet_data(holder->Packet(), i, &data, &length); if (r == -1) { WEBM_DEBUG("nestegg_packet_data failed r=%d", r); - return false; + return NS_ERROR_DOM_MEDIA_DEMUXER_ERR; } bool isKeyframe = false; if (aType == TrackInfo::kAudioTrack) { @@ -636,29 +643,31 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl // Packet is encrypted, can't peek, use packet info isKeyframe = nestegg_packet_has_keyframe(holder->Packet()) == NESTEGG_PACKET_HAS_KEYFRAME_TRUE; } else { - vpx_codec_stream_info_t si; - PodZero(&si); - si.sz = sizeof(si); + auto sample = MakeSpan(data, length); switch (mVideoCodec) { case NESTEGG_CODEC_VP8: - vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), data, length, &si); + isKeyframe = VPXDecoder::IsKeyframe(sample, VPXDecoder::Codec::VP8); break; case NESTEGG_CODEC_VP9: - vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), data, length, &si); + isKeyframe = VPXDecoder::IsKeyframe(sample, VPXDecoder::Codec::VP9); break; + default: + NS_WARNING("Cannot detect keyframes in unknown WebM video codec"); + return NS_ERROR_FAILURE; } - isKeyframe = si.is_kf; if (isKeyframe) { - // We only look for resolution changes on keyframes for both VP8 and - // VP9. Other resolution changes are invalid. - if (mLastSeenFrameWidth.isSome() && mLastSeenFrameHeight.isSome() && - (si.w != mLastSeenFrameWidth.value() || - si.h != mLastSeenFrameHeight.value())) { - mInfo.mVideo.mDisplay = nsIntSize(si.w, si.h); + // For both VP8 and VP9, we only look for resolution changes + // on keyframes. Other resolution changes are invalid. + auto codec = mVideoCodec == NESTEGG_CODEC_VP8 + ? VPXDecoder::Codec::VP8 + : VPXDecoder::Codec::VP9; + auto dimensions = VPXDecoder::GetFrameSize(sample, codec); + if (mLastSeenFrameSize.isSome() + && (dimensions != mLastSeenFrameSize.value())) { + mInfo.mVideo.mDisplay = dimensions; mSharedVideoTrackInfo = new SharedTrackInfo(mInfo.mVideo, ++sStreamSourceID); } - mLastSeenFrameWidth = Some(si.w); - mLastSeenFrameHeight = Some(si.h); + mLastSeenFrameSize = Some(dimensions); } } } @@ -668,7 +677,7 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl RefPtr<MediaRawData> sample = new MediaRawData(data, length); if (length && !sample->Data()) { // OOM. - return false; + return NS_ERROR_OUT_OF_MEMORY; } sample->mTimecode = tstamp; sample->mTime = tstamp; @@ -721,11 +730,12 @@ WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSampl } aSamples->Push(sample); } - return true; + return NS_OK; } -RefPtr<NesteggPacketHolder> -WebMDemuxer::NextPacket(TrackInfo::TrackType aType) +nsresult +WebMDemuxer::NextPacket(TrackInfo::TrackType aType, + RefPtr<NesteggPacketHolder>& aPacket) { bool isVideo = aType == TrackInfo::kVideoTrack; @@ -734,56 +744,64 @@ WebMDemuxer::NextPacket(TrackInfo::TrackType aType) bool hasType = isVideo ? mHasVideo : mHasAudio; if (!hasType) { - return nullptr; + return NS_ERROR_DOM_MEDIA_DEMUXER_ERR; } // The packet queue for the type that we are interested in. WebMPacketQueue &packets = isVideo ? mVideoPackets : mAudioPackets; if (packets.GetSize() > 0) { - return packets.PopFront(); + aPacket = packets.PopFront(); + return NS_OK; } // Track we are interested in uint32_t ourTrack = isVideo ? mVideoTrack : mAudioTrack; do { - RefPtr<NesteggPacketHolder> holder = DemuxPacket(aType); + RefPtr<NesteggPacketHolder> holder; + nsresult rv = DemuxPacket(aType, holder); + if (NS_FAILED(rv)) { + return rv; + } if (!holder) { - return nullptr; + return NS_ERROR_DOM_MEDIA_DEMUXER_ERR; } if (ourTrack == holder->Track()) { - return holder; + aPacket = holder; + return NS_OK; } } while (true); } -RefPtr<NesteggPacketHolder> -WebMDemuxer::DemuxPacket(TrackInfo::TrackType aType) +nsresult +WebMDemuxer::DemuxPacket(TrackInfo::TrackType aType, + RefPtr<NesteggPacketHolder>& aPacket) { nestegg_packet* packet; int r = nestegg_read_packet(Context(aType), &packet); if (r == 0) { nestegg_read_reset(Context(aType)); - return nullptr; + return NS_ERROR_DOM_MEDIA_END_OF_STREAM; } else if (r < 0) { - return nullptr; + return NS_ERROR_DOM_MEDIA_DEMUXER_ERR; } unsigned int track = 0; r = nestegg_packet_track(packet, &track); if (r == -1) { - return nullptr; + return NS_ERROR_DOM_MEDIA_DEMUXER_ERR; } int64_t offset = Resource(aType).Tell(); RefPtr<NesteggPacketHolder> holder = new NesteggPacketHolder(); if (!holder->Init(packet, offset, track, false)) { - return nullptr; + return NS_ERROR_DOM_MEDIA_DEMUXER_ERR; } - return holder; + aPacket = holder; + return NS_OK; } void @@ -943,7 +961,14 @@ WebMTrackDemuxer::Seek(media::TimeUnit aTime) media::TimeUnit seekTime = aTime; mSamples.Reset(); mParent->SeekInternal(mType, aTime); - mParent->GetNextPacket(mType, &mSamples); + nsresult rv = mParent->GetNextPacket(mType, &mSamples); + if (NS_FAILED(rv)) { + if (rv == NS_ERROR_DOM_MEDIA_END_OF_STREAM) { + // Ignore the error for now, the next GetSample will be rejected with EOS. + return SeekPromise::CreateAndResolve(media::TimeUnit(), __func__); + } + return SeekPromise::CreateAndReject(rv, __func__); + } mNeedKeyframe = true; // Check what time we actually seeked to. @@ -956,15 +981,18 @@ WebMTrackDemuxer::Seek(media::TimeUnit aTime) return SeekPromise::CreateAndResolve(seekTime, __func__); } -RefPtr<MediaRawData> -WebMTrackDemuxer::NextSample() +nsresult +WebMTrackDemuxer::NextSample(RefPtr<MediaRawData>& aData) { - while (mSamples.GetSize() < 1 && mParent->GetNextPacket(mType, &mSamples)) { + nsresult rv; + while (mSamples.GetSize() < 1 && + NS_SUCCEEDED((rv = mParent->GetNextPacket(mType, &mSamples)))) { } if (mSamples.GetSize()) { - return mSamples.PopFront(); + aData = mSamples.PopFront(); + return NS_OK; } - return nullptr; + return rv; } RefPtr<WebMTrackDemuxer::SamplesPromise> @@ -973,9 +1001,12 @@ WebMTrackDemuxer::GetSamples(int32_t aNumSamples) RefPtr<SamplesHolder> samples = new SamplesHolder; MOZ_ASSERT(aNumSamples); + nsresult rv = NS_ERROR_DOM_MEDIA_END_OF_STREAM; + while (aNumSamples) { - RefPtr<MediaRawData> sample(NextSample()); - if (!sample) { + RefPtr<MediaRawData> sample; + rv = NextSample(sample); + if (NS_FAILED(rv)) { break; } if (mNeedKeyframe && !sample->mKeyframe) { @@ -987,7 +1018,7 @@ WebMTrackDemuxer::GetSamples(int32_t aNumSamples) } if (samples->mSamples.IsEmpty()) { - return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__); + return SamplesPromise::CreateAndReject(rv, __func__); } else { UpdateSamples(samples->mSamples); return SamplesPromise::CreateAndResolve(samples, __func__); @@ -1022,7 +1053,8 @@ WebMTrackDemuxer::SetNextKeyFrameTime() } // Demux and buffer frames until we find a keyframe. RefPtr<MediaRawData> sample; - while (!foundKeyframe && (sample = NextSample())) { + nsresult rv = NS_OK; + while (!foundKeyframe && NS_SUCCEEDED((rv = NextSample(sample)))) { if (sample->mKeyframe) { frameTime = sample->mTime; foundKeyframe = true; @@ -1104,10 +1136,11 @@ WebMTrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold) uint32_t parsed = 0; bool found = false; RefPtr<MediaRawData> sample; + nsresult rv = NS_OK; int64_t sampleTime; WEBM_DEBUG("TimeThreshold: %f", aTimeThreshold.ToSeconds()); - while (!found && (sample = NextSample())) { + while (!found && NS_SUCCEEDED((rv = NextSample(sample)))) { parsed++; sampleTime = sample->mTime; if (sample->mKeyframe && sampleTime >= aTimeThreshold.ToMicroseconds()) { @@ -1116,7 +1149,9 @@ WebMTrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold) mSamples.PushFront(sample.forget()); } } - SetNextKeyFrameTime(); + if (NS_SUCCEEDED(rv)) { + SetNextKeyFrameTime(); + } if (found) { WEBM_DEBUG("next sample: %f (parsed: %d)", media::TimeUnit::FromMicroseconds(sampleTime).ToSeconds(), diff --git a/dom/media/webm/WebMDemuxer.h b/dom/media/webm/WebMDemuxer.h index 6fff38e7d..09780e8d3 100644 --- a/dom/media/webm/WebMDemuxer.h +++ b/dom/media/webm/WebMDemuxer.h @@ -111,7 +111,8 @@ public: bool GetOffsetForTime(uint64_t aTime, int64_t* aOffset); // Demux next WebM packet and append samples to MediaRawDataQueue - bool GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSamples); + nsresult GetNextPacket(TrackInfo::TrackType aType, + MediaRawDataQueue *aSamples); nsresult Reset(TrackInfo::TrackType aType); @@ -175,11 +176,13 @@ private: // Read a packet from the nestegg file. Returns nullptr if all packets for // the particular track have been read. Pass TrackInfo::kVideoTrack or // TrackInfo::kVideoTrack to indicate the type of the packet we want to read. - RefPtr<NesteggPacketHolder> NextPacket(TrackInfo::TrackType aType); + nsresult NextPacket(TrackInfo::TrackType aType, + RefPtr<NesteggPacketHolder>& aPacket); // Internal method that demuxes the next packet from the stream. The caller // is responsible for making sure it doesn't get lost. - RefPtr<NesteggPacketHolder> DemuxPacket(TrackInfo::TrackType aType); + nsresult DemuxPacket(TrackInfo::TrackType aType, + RefPtr<NesteggPacketHolder>& aPacket); // libnestegg audio and video context for webm container. // Access on reader's thread only. @@ -237,8 +240,7 @@ private: int64_t mLastWebMBlockOffset; const bool mIsMediaSource; - Maybe<uint32_t> mLastSeenFrameWidth; - Maybe<uint32_t> mLastSeenFrameHeight; + Maybe<nsIntSize> mLastSeenFrameSize; // This will be populated only if a resolution change occurs, otherwise it // will be left as null so the original metadata is used RefPtr<SharedTrackInfo> mSharedVideoTrackInfo; @@ -276,7 +278,7 @@ private: ~WebMTrackDemuxer(); void UpdateSamples(nsTArray<RefPtr<MediaRawData>>& aSamples); void SetNextKeyFrameTime(); - RefPtr<MediaRawData> NextSample (); + nsresult NextSample(RefPtr<MediaRawData>& aData); RefPtr<WebMDemuxer> mParent; TrackInfo::TrackType mType; UniquePtr<TrackInfo> mInfo; |