Bug 1163486 - Update test to use new MP4Demuxer. r=bholley

This commit is contained in:
Alfredo Yang 2015-07-21 02:49:00 +02:00
Родитель 8393a2233e
Коммит c70bac0966
2 изменённых файлов: 197 добавлений и 72 удалений

Просмотреть файл

@ -4,32 +4,151 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "mp4_demuxer/mp4_demuxer.h" #include "MP4Demuxer.h"
#include "MP4Stream.h" #include "MP4Stream.h"
#include "MozPromise.h"
#include "MediaDataDemuxer.h"
#include "SharedThreadPool.h"
#include "TaskQueue.h"
#include "mozilla/ArrayUtils.h" #include "mozilla/ArrayUtils.h"
#include "MockMediaResource.h" #include "MockMediaResource.h"
using namespace mozilla; using namespace mozilla;
using namespace mp4_demuxer; using namespace mp4_demuxer;
class AutoTaskQueue;
#define DO_FAIL []()->void { EXPECT_TRUE(false); }
class MP4DemuxerBinding class MP4DemuxerBinding
{ {
public: public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MP4DemuxerBinding); NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MP4DemuxerBinding);
nsRefPtr<MockMediaResource> resource; nsRefPtr<MockMediaResource> resource;
Monitor mMonitor; nsRefPtr<MP4Demuxer> mDemuxer;
nsRefPtr<MP4Demuxer> demuxer; nsRefPtr<TaskQueue> mTaskQueue;
nsRefPtr<MediaTrackDemuxer> mAudioTrack;
nsRefPtr<MediaTrackDemuxer> mVideoTrack;
uint32_t mIndex;
nsTArray<nsRefPtr<MediaRawData>> mSamples;
nsTArray<int64_t> mKeyFrameTimecodes;
MozPromiseHolder<GenericPromise> mCheckTrackKeyFramePromise;
MozPromiseHolder<GenericPromise> mCheckTrackSamples;
explicit MP4DemuxerBinding(const char* aFileName = "dash_dashinit.mp4") explicit MP4DemuxerBinding(const char* aFileName = "dash_dashinit.mp4")
: resource(new MockMediaResource(aFileName)) : resource(new MockMediaResource(aFileName))
, mMonitor("TestMP4Demuxer monitor") , mDemuxer(new MP4Demuxer(resource))
, demuxer(new MP4Demuxer(new MP4Stream(resource), &mMonitor)) , mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK)))
, mIndex(0)
{ {
EXPECT_EQ(NS_OK, resource->Open(nullptr)); EXPECT_EQ(NS_OK, resource->Open(nullptr));
} }
template<typename Function>
void RunTestAndWait(const Function& aFunction)
{
Function func(aFunction);
mDemuxer->Init()->Then(mTaskQueue, __func__, Move(func), DO_FAIL);
mTaskQueue->AwaitShutdownAndIdle();
}
nsRefPtr<GenericPromise>
CheckTrackKeyFrame(MediaTrackDemuxer* aTrackDemuxer)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
nsRefPtr<MediaTrackDemuxer> track = aTrackDemuxer;
nsRefPtr<MP4DemuxerBinding> self = this;
int64_t time = -1;
while (mIndex < mSamples.Length()) {
uint32_t i = mIndex++;
if (mSamples[i]->mKeyframe) {
time = mSamples[i]->mTime;
break;
}
}
nsRefPtr<GenericPromise> p = mCheckTrackKeyFramePromise.Ensure(__func__);
if (time == -1) {
mCheckTrackKeyFramePromise.Resolve(true, __func__);
return p;
}
DispatchTask(
[track, time, self] () {
track->Seek(media::TimeUnit::FromMicroseconds(time))->Then(self->mTaskQueue, __func__,
[track, time, self] () {
track->GetSamples()->Then(self->mTaskQueue, __func__,
[track, time, self] (nsRefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) {
EXPECT_EQ(time, aSamples->mSamples[0]->mTime);
self->CheckTrackKeyFrame(track);
},
DO_FAIL
);
},
DO_FAIL
);
}
);
return p;
}
nsRefPtr<GenericPromise>
CheckTrackSamples(MediaTrackDemuxer* aTrackDemuxer)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
nsRefPtr<MediaTrackDemuxer> track = aTrackDemuxer;
nsRefPtr<MP4DemuxerBinding> self = this;
nsRefPtr<GenericPromise> p = mCheckTrackSamples.Ensure(__func__);
DispatchTask(
[track, self] () {
track->GetSamples()->Then(self->mTaskQueue, __func__,
[track, self] (nsRefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) {
if (aSamples->mSamples.Length()) {
self->mSamples.AppendElements(aSamples->mSamples);
self->CheckTrackSamples(track);
}
},
[self] (DemuxerFailureReason aReason) {
if (aReason == DemuxerFailureReason::DEMUXER_ERROR) {
EXPECT_TRUE(false);
self->mCheckTrackSamples.Reject(NS_ERROR_FAILURE, __func__);
} else if (aReason == DemuxerFailureReason::END_OF_STREAM) {
EXPECT_TRUE(self->mSamples.Length() > 1);
for (uint32_t i = 0; i < (self->mSamples.Length() - 1); i++) {
EXPECT_LT(self->mSamples[i]->mTimecode, self->mSamples[i + 1]->mTimecode);
if (self->mSamples[i]->mKeyframe) {
self->mKeyFrameTimecodes.AppendElement(self->mSamples[i]->mTimecode);
}
}
self->mCheckTrackSamples.Resolve(true, __func__);
}
}
);
}
);
return p;
}
private: private:
template<typename FunctionType>
void
DispatchTask(FunctionType aFun)
{
nsRefPtr<nsRunnable> r = NS_NewRunnableFunction(aFun);
mTaskQueue->Dispatch(r.forget());
}
virtual ~MP4DemuxerBinding() virtual ~MP4DemuxerBinding()
{ {
} }
@ -37,30 +156,20 @@ private:
TEST(MP4Demuxer, Seek) TEST(MP4Demuxer, Seek)
{ {
nsRefPtr<MP4DemuxerBinding> b = new MP4DemuxerBinding(); nsRefPtr<MP4DemuxerBinding> binding = new MP4DemuxerBinding();
MonitorAutoLock mon(b->mMonitor);
MP4Demuxer* d = b->demuxer;
EXPECT_TRUE(d->Init()); binding->RunTestAndWait([binding] () {
binding->mVideoTrack = binding->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
nsTArray<nsRefPtr<MediaRawData>> samples; binding->CheckTrackSamples(binding->mVideoTrack)
nsRefPtr<MediaRawData> sample; ->Then(binding->mTaskQueue, __func__,
while (!!(sample = d->DemuxVideoSample())) { [binding] () {
samples.AppendElement(sample); binding->CheckTrackKeyFrame(binding->mVideoTrack)
if (samples.Length() >= 2) { ->Then(binding->mTaskQueue, __func__,
EXPECT_LT(samples[samples.Length() - 2]->mTimecode, [binding] () {
samples[samples.Length() - 1]->mTimecode); binding->mTaskQueue->BeginShutdown();
} }, DO_FAIL);
} }, DO_FAIL);
Microseconds keyFrame = 0; });
for (size_t i = 0; i < samples.Length(); i++) {
if (samples[i]->mKeyframe) {
keyFrame = samples[i]->mTimecode;
}
d->SeekVideo(samples[i]->mTime);
sample = d->DemuxVideoSample();
EXPECT_EQ(keyFrame, sample->mTimecode);
}
} }
static nsCString static nsCString
@ -87,14 +196,10 @@ ToCryptoString(const CryptoSample& aCrypto)
return res; return res;
} }
#ifndef XP_WIN // VC2013 doesn't support C++11 array initialization.
TEST(MP4Demuxer, CENCFrag) TEST(MP4Demuxer, CENCFrag)
{ {
nsRefPtr<MP4DemuxerBinding> b = new MP4DemuxerBinding("gizmo-frag.mp4");
MonitorAutoLock mon(b->mMonitor);
MP4Demuxer* d = b->demuxer;
EXPECT_TRUE(d->Init());
const char* video[] = { const char* video[] = {
"1 16 7e571d037e571d037e571d037e571d03 00000000000000000000000000000000 5,684 5,16980", "1 16 7e571d037e571d037e571d037e571d03 00000000000000000000000000000000 5,684 5,16980",
"1 16 7e571d037e571d037e571d037e571d03 00000000000000000000000000000450 5,1826", "1 16 7e571d037e571d037e571d037e571d03 00000000000000000000000000000450 5,1826",
@ -158,13 +263,22 @@ TEST(MP4Demuxer, CENCFrag)
"1 16 7e571d037e571d037e571d037e571d03 000000000000000000000000000019cd 5,2392", "1 16 7e571d037e571d037e571d037e571d03 000000000000000000000000000019cd 5,2392",
}; };
nsRefPtr<MediaRawData> sample; nsRefPtr<MP4DemuxerBinding> binding = new MP4DemuxerBinding("gizmo-frag.mp4");
size_t i = 0;
while (!!(sample = d->DemuxVideoSample())) { binding->RunTestAndWait([binding, video] () {
nsCString text = ToCryptoString(sample->mCrypto); // grab all video samples.
EXPECT_STREQ(video[i++], text.get()); binding->mVideoTrack = binding->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
} binding->CheckTrackSamples(binding->mVideoTrack)
EXPECT_EQ(ArrayLength(video), i); ->Then(binding->mTaskQueue, __func__,
[binding, video] () {
for (uint32_t i = 0; i < binding->mSamples.Length(); i++) {
nsCString text = ToCryptoString(binding->mSamples[i]->mCrypto);
EXPECT_STREQ(video[i++], text.get());
}
EXPECT_EQ(ArrayLength(video), binding->mSamples.Length());
binding->mTaskQueue->BeginShutdown();
}, DO_FAIL);
});
const char* audio[] = { const char* audio[] = {
"1 16 7e571d047e571d047e571d047e571d04 00000000000000000000000000000000 0,281", "1 16 7e571d047e571d047e571d047e571d04 00000000000000000000000000000000 0,281",
@ -262,42 +376,52 @@ TEST(MP4Demuxer, CENCFrag)
"1 16 7e571d047e571d047e571d047e571d04 000000000000000000000000000008cd 0,433", "1 16 7e571d047e571d047e571d047e571d04 000000000000000000000000000008cd 0,433",
"1 16 7e571d047e571d047e571d047e571d04 000000000000000000000000000008e9 0,481", "1 16 7e571d047e571d047e571d047e571d04 000000000000000000000000000008e9 0,481",
}; };
nsRefPtr<MP4DemuxerBinding> audiobinding = new MP4DemuxerBinding("gizmo-frag.mp4");
i = 0; audiobinding->RunTestAndWait([audiobinding, audio] () {
while (!!(sample = d->DemuxAudioSample())) { // grab all audio samples.
nsCString text = ToCryptoString(sample->mCrypto); audiobinding->mAudioTrack = audiobinding->mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
EXPECT_STREQ(audio[i++], text.get()); audiobinding->CheckTrackSamples(audiobinding->mAudioTrack)
} ->Then(audiobinding->mTaskQueue, __func__,
EXPECT_EQ(ArrayLength(audio), i); [audiobinding, audio] () {
EXPECT_TRUE(audiobinding->mSamples.Length() > 1);
for (uint32_t i = 0; i < audiobinding->mSamples.Length(); i++) {
nsCString text = ToCryptoString(audiobinding->mSamples[i]->mCrypto);
EXPECT_STREQ(audio[i++], text.get());
}
EXPECT_EQ(ArrayLength(audio), audiobinding->mSamples.Length());
audiobinding->mTaskQueue->BeginShutdown();
}, DO_FAIL);
});
} }
#endif
TEST(MP4Demuxer, GetNextKeyframe) TEST(MP4Demuxer, GetNextKeyframe)
{ {
nsRefPtr<MP4DemuxerBinding> b = new MP4DemuxerBinding("gizmo-frag.mp4"); nsRefPtr<MP4DemuxerBinding> binding = new MP4DemuxerBinding("gizmo-frag.mp4");
MonitorAutoLock mon(b->mMonitor);
MP4Demuxer* d = b->demuxer;
EXPECT_TRUE(d->Init()); binding->RunTestAndWait([binding] () {
// Insert a [0,end] buffered range, to simulate Moof's being buffered
// via MSE.
auto len = binding->resource->GetLength();
binding->resource->MockAddBufferedRange(0, len);
// Insert a [0,end] buffered range, to simulate Moof's being buffered // gizmp-frag has two keyframes; one at dts=cts=0, and another at
// via MSE. // dts=cts=1000000. Verify we get expected results.
auto len = b->resource->GetLength(); media::TimeUnit time;
b->resource->MockAddBufferedRange(0, len); binding->mVideoTrack = binding->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
binding->mVideoTrack->Reset();
// Rebuild the index so that it can be used to find the keyframes. binding->mVideoTrack->GetNextRandomAccessPoint(&time);
nsTArray<MediaByteRange> ranges; EXPECT_EQ(time.ToMicroseconds(), 0);
EXPECT_TRUE(NS_SUCCEEDED(b->resource->GetCachedRanges(ranges))); binding->mVideoTrack->GetSamples()->Then(binding->mTaskQueue, __func__,
d->UpdateIndex(ranges); [binding] () {
media::TimeUnit time;
// gizmp-frag has two keyframes; one at dts=cts=0, and another at binding->mVideoTrack->GetNextRandomAccessPoint(&time);
// dts=cts=1000000. Verify we get expected results. EXPECT_EQ(time.ToMicroseconds(), 1000000);
binding->mTaskQueue->BeginShutdown();
nsRefPtr<MediaRawData> sample; },
size_t i = 0; DO_FAIL
const int64_t keyframe = 1000000; );
while (!!(sample = d->DemuxVideoSample())) { });
int64_t expected = (sample->mTimecode < keyframe) ? keyframe : -1;
EXPECT_EQ(d->GetNextKeyframeTime(), expected);
i++;
}
} }

Просмотреть файл

@ -12,6 +12,7 @@ UNIFIED_SOURCES += [
'TestIntervalSet.cpp', 'TestIntervalSet.cpp',
'TestMozPromise.cpp', 'TestMozPromise.cpp',
'TestMP3Demuxer.cpp', 'TestMP3Demuxer.cpp',
'TestMP4Demuxer.cpp',
# 'TestMP4Reader.cpp', disabled so we can turn check tests back on (bug 1175752) # 'TestMP4Reader.cpp', disabled so we can turn check tests back on (bug 1175752)
'TestTrackEncoder.cpp', 'TestTrackEncoder.cpp',
'TestVideoSegment.cpp', 'TestVideoSegment.cpp',