summaryrefslogtreecommitdiffstats
path: root/dom/media
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media')
-rw-r--r--dom/media/MediaData.h3
-rw-r--r--dom/media/platforms/agnostic/VPXDecoder.cpp53
-rw-r--r--dom/media/platforms/agnostic/VPXDecoder.h12
-rw-r--r--dom/media/webm/WebMDemuxer.cpp145
-rw-r--r--dom/media/webm/WebMDemuxer.h14
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;