зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1163486 - Update test to use new MP4Demuxer. r=bholley
This commit is contained in:
Родитель
8393a2233e
Коммит
c70bac0966
|
@ -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',
|
||||||
|
|
Загрузка…
Ссылка в новой задаче