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
parent d322b99ad7
commit 70882cfcd4
2 changed files with 197 additions and 72 deletions

View File

@ -4,32 +4,151 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include "mp4_demuxer/mp4_demuxer.h"
#include "MP4Demuxer.h"
#include "MP4Stream.h"
#include "MozPromise.h"
#include "MediaDataDemuxer.h"
#include "SharedThreadPool.h"
#include "TaskQueue.h"
#include "mozilla/ArrayUtils.h"
#include "MockMediaResource.h"
using namespace mozilla;
using namespace mp4_demuxer;
class AutoTaskQueue;
#define DO_FAIL []()->void { EXPECT_TRUE(false); }
class MP4DemuxerBinding
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MP4DemuxerBinding);
nsRefPtr<MockMediaResource> resource;
Monitor mMonitor;
nsRefPtr<MP4Demuxer> demuxer;
nsRefPtr<MP4Demuxer> mDemuxer;
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")
: resource(new MockMediaResource(aFileName))
, mMonitor("TestMP4Demuxer monitor")
, demuxer(new MP4Demuxer(new MP4Stream(resource), &mMonitor))
, mDemuxer(new MP4Demuxer(resource))
, mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK)))
, mIndex(0)
{
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:
template<typename FunctionType>
void
DispatchTask(FunctionType aFun)
{
nsRefPtr<nsRunnable> r = NS_NewRunnableFunction(aFun);
mTaskQueue->Dispatch(r.forget());
}
virtual ~MP4DemuxerBinding()
{
}
@ -37,30 +156,20 @@ private:
TEST(MP4Demuxer, Seek)
{
nsRefPtr<MP4DemuxerBinding> b = new MP4DemuxerBinding();
MonitorAutoLock mon(b->mMonitor);
MP4Demuxer* d = b->demuxer;
nsRefPtr<MP4DemuxerBinding> binding = new MP4DemuxerBinding();
EXPECT_TRUE(d->Init());
nsTArray<nsRefPtr<MediaRawData>> samples;
nsRefPtr<MediaRawData> sample;
while (!!(sample = d->DemuxVideoSample())) {
samples.AppendElement(sample);
if (samples.Length() >= 2) {
EXPECT_LT(samples[samples.Length() - 2]->mTimecode,
samples[samples.Length() - 1]->mTimecode);
}
}
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);
}
binding->RunTestAndWait([binding] () {
binding->mVideoTrack = binding->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
binding->CheckTrackSamples(binding->mVideoTrack)
->Then(binding->mTaskQueue, __func__,
[binding] () {
binding->CheckTrackKeyFrame(binding->mVideoTrack)
->Then(binding->mTaskQueue, __func__,
[binding] () {
binding->mTaskQueue->BeginShutdown();
}, DO_FAIL);
}, DO_FAIL);
});
}
static nsCString
@ -87,14 +196,10 @@ ToCryptoString(const CryptoSample& aCrypto)
return res;
}
#ifndef XP_WIN // VC2013 doesn't support C++11 array initialization.
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[] = {
"1 16 7e571d037e571d037e571d037e571d03 00000000000000000000000000000000 5,684 5,16980",
"1 16 7e571d037e571d037e571d037e571d03 00000000000000000000000000000450 5,1826",
@ -158,13 +263,22 @@ TEST(MP4Demuxer, CENCFrag)
"1 16 7e571d037e571d037e571d037e571d03 000000000000000000000000000019cd 5,2392",
};
nsRefPtr<MediaRawData> sample;
size_t i = 0;
while (!!(sample = d->DemuxVideoSample())) {
nsCString text = ToCryptoString(sample->mCrypto);
EXPECT_STREQ(video[i++], text.get());
}
EXPECT_EQ(ArrayLength(video), i);
nsRefPtr<MP4DemuxerBinding> binding = new MP4DemuxerBinding("gizmo-frag.mp4");
binding->RunTestAndWait([binding, video] () {
// grab all video samples.
binding->mVideoTrack = binding->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
binding->CheckTrackSamples(binding->mVideoTrack)
->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[] = {
"1 16 7e571d047e571d047e571d047e571d04 00000000000000000000000000000000 0,281",
@ -262,42 +376,52 @@ TEST(MP4Demuxer, CENCFrag)
"1 16 7e571d047e571d047e571d047e571d04 000000000000000000000000000008cd 0,433",
"1 16 7e571d047e571d047e571d047e571d04 000000000000000000000000000008e9 0,481",
};
nsRefPtr<MP4DemuxerBinding> audiobinding = new MP4DemuxerBinding("gizmo-frag.mp4");
i = 0;
while (!!(sample = d->DemuxAudioSample())) {
nsCString text = ToCryptoString(sample->mCrypto);
EXPECT_STREQ(audio[i++], text.get());
}
EXPECT_EQ(ArrayLength(audio), i);
audiobinding->RunTestAndWait([audiobinding, audio] () {
// grab all audio samples.
audiobinding->mAudioTrack = audiobinding->mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
audiobinding->CheckTrackSamples(audiobinding->mAudioTrack)
->Then(audiobinding->mTaskQueue, __func__,
[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)
{
nsRefPtr<MP4DemuxerBinding> b = new MP4DemuxerBinding("gizmo-frag.mp4");
MonitorAutoLock mon(b->mMonitor);
MP4Demuxer* d = b->demuxer;
nsRefPtr<MP4DemuxerBinding> binding = new MP4DemuxerBinding("gizmo-frag.mp4");
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
// via MSE.
auto len = b->resource->GetLength();
b->resource->MockAddBufferedRange(0, len);
// Rebuild the index so that it can be used to find the keyframes.
nsTArray<MediaByteRange> ranges;
EXPECT_TRUE(NS_SUCCEEDED(b->resource->GetCachedRanges(ranges)));
d->UpdateIndex(ranges);
// gizmp-frag has two keyframes; one at dts=cts=0, and another at
// dts=cts=1000000. Verify we get expected results.
nsRefPtr<MediaRawData> sample;
size_t i = 0;
const int64_t keyframe = 1000000;
while (!!(sample = d->DemuxVideoSample())) {
int64_t expected = (sample->mTimecode < keyframe) ? keyframe : -1;
EXPECT_EQ(d->GetNextKeyframeTime(), expected);
i++;
}
// gizmp-frag has two keyframes; one at dts=cts=0, and another at
// dts=cts=1000000. Verify we get expected results.
media::TimeUnit time;
binding->mVideoTrack = binding->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
binding->mVideoTrack->Reset();
binding->mVideoTrack->GetNextRandomAccessPoint(&time);
EXPECT_EQ(time.ToMicroseconds(), 0);
binding->mVideoTrack->GetSamples()->Then(binding->mTaskQueue, __func__,
[binding] () {
media::TimeUnit time;
binding->mVideoTrack->GetNextRandomAccessPoint(&time);
EXPECT_EQ(time.ToMicroseconds(), 1000000);
binding->mTaskQueue->BeginShutdown();
},
DO_FAIL
);
});
}

View File

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