Handle MpdNotifyMuxerListener::OnSampleDurationReady() for VOD

- Saves sample_duration in the media_info protobuf.
- When MpdNotifyMuxerListener::OnMediaEnd() is called, media_info passed
  to MpdNotifier::NotifyNewContainer() should contain the sample
  duration.
- Add tests for MpdNotifyMuxerListener.
- Moved common code from vod_media_info_dump_muxer_listener_unittest.cc
  and mpd_notify_muxer_listener_unittest.cc to
  muxer_listener_test_helper.cc.

Change-Id: Ibf2340076d45828d26f1af54a1ed8e95e5884c23
This commit is contained in:
Rintaro Kuroiwa 2015-07-29 17:42:11 -07:00
parent c48a94d60f
commit fca2bb1e44
6 changed files with 483 additions and 117 deletions

View File

@ -33,11 +33,15 @@
'target_name': 'media_event_unittest',
'type': '<(gtest_target_type)',
'sources': [
'mpd_notify_muxer_listener_unittest.cc',
'muxer_listener_test_helper.cc',
'muxer_listener_test_helper.h',
'vod_media_info_dump_muxer_listener_unittest.cc',
],
'dependencies': [
'../../base/base.gyp:base',
'../../mpd/mpd.gyp:media_info_proto',
'../../testing/gmock.gyp:gmock',
'../../testing/gtest.gyp:gtest',
'../../testing/gtest.gyp:gtest_main',
# Depends on full protobuf to read/write with TextFormat.

View File

@ -73,9 +73,29 @@ void MpdNotifyMuxerListener::OnMediaStart(
}
}
// Record the sample duration in the media info for VOD so that OnMediaEnd, all
// the information is in the media info.
void MpdNotifyMuxerListener::OnSampleDurationReady(
uint32_t sample_duration) {
mpd_notifier_->NotifySampleDuration(notification_id_, sample_duration);
if (mpd_notifier_->dash_profile() == kLiveProfile) {
mpd_notifier_->NotifySampleDuration(notification_id_, sample_duration);
return;
}
if (!media_info_) {
LOG(WARNING) << "Got sample duration " << sample_duration
<< " but no media was specified.";
return;
}
if (!media_info_->has_video_info()) {
// If non video, don't worry about it (at the moment).
return;
}
if (media_info_->video_info().has_frame_duration()) {
return;
}
media_info_->mutable_video_info()->set_frame_duration(sample_duration);
}
void MpdNotifyMuxerListener::OnMediaEnd(bool has_init_range,
@ -110,6 +130,9 @@ void MpdNotifyMuxerListener::OnMediaEnd(bool has_init_range,
void MpdNotifyMuxerListener::OnNewSegment(uint64_t start_time,
uint64_t duration,
uint64_t segment_file_size) {
// TODO(rkuriowa): MpdNotifier::NotifyNewSegment() should be called for VOD as
// well. The easiest way to do this is to save all the values and call it
// after NotifyNewContainer() in OnMediaEnd().
if (mpd_notifier_->dash_profile() != kLiveProfile) return;
// TODO(kqyang): Check return result.
mpd_notifier_->NotifyNewSegment(

View File

@ -0,0 +1,229 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/event/mpd_notify_muxer_listener.h"
#include <gmock/gmock.h>
#include <google/protobuf/text_format.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <vector>
#include "packager/base/stl_util.h"
#include "packager/media/base/video_stream_info.h"
#include "packager/media/event/muxer_listener_test_helper.h"
#include "packager/mpd/base/content_protection_element.h"
#include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/mpd_notifier.h"
using ::testing::_;
namespace edash_packager {
namespace {
// TODO(rkuroiwa): This is copied from mpd_builder_test_helper.cc. Make a
// common target that only has mpd_builder_test_helper and its dependencies
// so the two test targets can share this.
MediaInfo ConvertToMediaInfo(const std::string& media_info_string) {
MediaInfo media_info;
CHECK(::google::protobuf::TextFormat::ParseFromString(media_info_string,
&media_info));
return media_info;
}
class MockMpdNotifier : public MpdNotifier {
public:
MockMpdNotifier(DashProfile profile) : MpdNotifier(profile) {}
virtual ~MockMpdNotifier() OVERRIDE {}
MOCK_METHOD0(Init, bool());
MOCK_METHOD2(NotifyNewContainer,
bool(const MediaInfo& media_info, uint32_t* container_id));
MOCK_METHOD2(NotifySampleDuration,
bool(uint32_t container_id, uint32_t sample_duration));
MOCK_METHOD4(NotifyNewSegment,
bool(uint32_t container_id,
uint64_t start_time,
uint64_t duration,
uint64_t size));
MOCK_METHOD2(
AddContentProtectionElement,
bool(uint32_t container_id,
const ContentProtectionElement& content_protection_element));
};
} // namespace
namespace media {
class MpdNotifyMuxerListenerTest : public ::testing::Test {
public:
// Set up objects for VOD profile.
void SetupForVod() {
notifier_.reset(new MockMpdNotifier(kOnDemandProfile));
listener_.reset(new MpdNotifyMuxerListener(notifier_.get()));
}
void FireOnMediaEndWithParams(const OnMediaEndParameters& params) {
// On success, this writes the result to |temp_file_path_|.
listener_->OnMediaEnd(params.has_init_range,
params.init_range_start,
params.init_range_end,
params.has_index_range,
params.index_range_start,
params.index_range_end,
params.duration_seconds,
params.file_size);
}
scoped_ptr<MpdNotifyMuxerListener> listener_;
scoped_ptr<MockMpdNotifier> notifier_;
};
MATCHER_P(ExpectMediaInfoEq, expected_text_format, "") {
const MediaInfo expected = ConvertToMediaInfo(expected_text_format);
return MediaInfoEqual(expected, arg);
}
TEST_F(MpdNotifyMuxerListenerTest, VodClearContent) {
SetupForVod();
MuxerOptions muxer_options;
SetDefaultMuxerOptionsValues(&muxer_options);
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
scoped_refptr<StreamInfo> video_stream_info =
CreateVideoStreamInfo(video_params);
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
listener_->OnMediaStart(muxer_options, *video_stream_info,
kDefaultReferenceTimeScale,
MuxerListener::kContainerMp4);
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
EXPECT_CALL(*notifier_, NotifyNewContainer(
ExpectMediaInfoEq(kExpectedDefaultMediaInfo), _));
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
}
// default_key_id and pssh are converted to string because when std::equal
// compares a negative char and uint8_t > 127, it considers them not equal.
MATCHER_P4(ProtectedContentEq, uuid, name, default_key_id, pssh, "") {
const MediaInfo& actual_media_info = arg;
EXPECT_TRUE(actual_media_info.has_protected_content());
EXPECT_EQ(
1,
actual_media_info.protected_content().content_protection_entry().size());
const std::string& actual_default_kid =
actual_media_info.protected_content().default_key_id();
const std::string expected_default_kid_string(default_key_id.begin(),
default_key_id.end());
if (actual_default_kid != expected_default_kid_string) {
return false;
}
const MediaInfo_ProtectedContent_ContentProtectionEntry& entry =
actual_media_info.protected_content().content_protection_entry(0);
const std::string expcted_pssh_string(pssh.begin(), pssh.end());
return entry.uuid() == uuid && entry.name_version() == name &&
entry.pssh().size() == pssh.size() &&
entry.pssh() == expcted_pssh_string;
}
TEST_F(MpdNotifyMuxerListenerTest, VodEncryptedContent) {
SetupForVod();
MuxerOptions muxer_options;
SetDefaultMuxerOptionsValues(&muxer_options);
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
scoped_refptr<StreamInfo> video_stream_info =
CreateVideoStreamInfo(video_params);
// Can be anystring, we just want to check that it is preserved in the
// protobuf.
const char kTestUUID[] = "somebogusuuid";
const char kDrmName[] = "drmname";
const char kDefaultKeyId[] = "defaultkeyid";
const char kPssh[] = "pssh";
const std::vector<uint8_t> default_key_id(
kDefaultKeyId, kDefaultKeyId + arraysize(kDefaultKeyId) - 1);
const std::vector<uint8_t> pssh(kPssh, kPssh + arraysize(kPssh) - 1);
const std::string kExpectedMediaInfo =
std::string(kExpectedDefaultMediaInfo) +
"protected_content {\n"
" content_protection_entry {\n"
" uuid: 'somebogusuuid'\n"
" name_version: 'drmname'\n"
" pssh: 'pssh'\n"
" }\n"
" default_key_id: 'defaultkeyid'\n"
"}\n";
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
listener_->OnEncryptionInfoReady(kTestUUID, kDrmName, default_key_id, pssh);
listener_->OnMediaStart(muxer_options, *video_stream_info,
kDefaultReferenceTimeScale,
MuxerListener::kContainerMp4);
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
EXPECT_CALL(*notifier_,
NotifyNewContainer(ExpectMediaInfoEq(kExpectedMediaInfo), _));
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
}
// Verify that calling OnSampleDurationReady() sets the frame duration in the
// media info, and the media info gets passed to NotifyNewContainer() with
// frame_duration == sample_duration.
TEST_F(MpdNotifyMuxerListenerTest, VodOnSampleDurationReady) {
SetupForVod();
MuxerOptions muxer_options;
SetDefaultMuxerOptionsValues(&muxer_options);
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
scoped_refptr<StreamInfo> video_stream_info =
CreateVideoStreamInfo(video_params);
const uint32_t kSampleDuration = 1234u;
const char kExpectedMediaInfo[] =
"bandwidth: 7620\n"
"video_info {\n"
" frame_duration: 1234\n" // Should match the constant above.
" codec: 'avc1.010101'\n"
" width: 720\n"
" height: 480\n"
" time_scale: 10\n"
" pixel_width: 1\n"
" pixel_height: 1\n"
"}\n"
"init_range {\n"
" begin: 0\n"
" end: 120\n"
"}\n"
"index_range {\n"
" begin: 121\n"
" end: 221\n"
"}\n"
"reference_time_scale: 1111\n"
"container_type: 1\n"
"media_file_name: 'test_output_file_name.mp4'\n"
"media_duration_seconds: 10.5\n";
const uint32_t kReferenceTimeScale = 1111u; // Should match the protobuf.
EXPECT_CALL(*notifier_, NotifyNewContainer(_, _)).Times(0);
listener_->OnMediaStart(muxer_options, *video_stream_info,
kReferenceTimeScale, MuxerListener::kContainerMp4);
listener_->OnSampleDurationReady(kSampleDuration);
::testing::Mock::VerifyAndClearExpectations(notifier_.get());
EXPECT_CALL(*notifier_,
NotifyNewContainer(ExpectMediaInfoEq(kExpectedMediaInfo), _));
FireOnMediaEndWithParams(GetDefaultOnMediaEndParams());
}
// TODO(rkuroiwa): Add tests for live.
} // namespace media
} // namespace edash_packager

View File

@ -0,0 +1,119 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/event/muxer_listener_test_helper.h"
#include <gtest/gtest.h>
#include "packager/base/stl_util.h"
namespace edash_packager {
namespace media {
VideoStreamInfoParameters::VideoStreamInfoParameters() {}
VideoStreamInfoParameters::~VideoStreamInfoParameters() {}
scoped_refptr<StreamInfo> CreateVideoStreamInfo(
const VideoStreamInfoParameters& param) {
return scoped_refptr<StreamInfo>(
new VideoStreamInfo(param.track_id,
param.time_scale,
param.duration,
param.codec,
param.codec_string,
param.language,
param.width,
param.height,
param.pixel_width,
param.pixel_height,
0, // trick_play_rate
param.nalu_length_size,
vector_as_array(&param.extra_data),
param.extra_data.size(),
param.is_encrypted));
}
VideoStreamInfoParameters GetDefaultVideoStreamInfoParams() {
const int kTrackId = 0;
const uint32_t kTimeScale = 10;
const uint64_t kVideoStreamDuration = 200;
const VideoCodec kH264Codec = kCodecH264;
const uint8_t kH264Profile = 1;
const uint8_t kH264CompatibleProfile = 1;
const uint8_t kH264Level = 1;
const char* kLanuageUndefined = "und";
const uint16_t kWidth = 720;
const uint16_t kHeight = 480;
const uint32_t kPixelWidth = 1;
const uint32_t kPixelHeight = 1;
const uint8_t kNaluLengthSize = 1;
const std::vector<uint8_t> kExtraData;
const bool kEncryptedFlag = false;
VideoStreamInfoParameters params;
params.track_id = kTrackId;
params.time_scale = kTimeScale;
params.duration = kVideoStreamDuration;
params.codec = kH264Codec;
params.codec_string = VideoStreamInfo::GetCodecString(
kCodecH264, kH264Profile, kH264CompatibleProfile, kH264Level);
params.language = kLanuageUndefined;
params.width = kWidth;
params.height = kHeight;
params.pixel_width = kPixelWidth;
params.pixel_height = kPixelHeight;
params.nalu_length_size = kNaluLengthSize;
params.extra_data = kExtraData;
params.is_encrypted = kEncryptedFlag;
return params;
}
OnMediaEndParameters GetDefaultOnMediaEndParams() {
// Values for {init, index} range {start, end} are arbitrary, but makes sure
// that it is monotonically increasing and contiguous.
const bool kHasInitRange = true;
const uint64_t kInitRangeStart = 0;
const uint64_t kInitRangeEnd = kInitRangeStart + 120;
const uint64_t kHasIndexRange = true;
const uint64_t kIndexRangeStart = kInitRangeEnd + 1;
const uint64_t kIndexRangeEnd = kIndexRangeStart + 100;
const float kMediaDuration = 10.5f;
const uint64_t kFileSize = 10000;
OnMediaEndParameters param = {
kHasInitRange, kInitRangeStart, kInitRangeEnd, kHasIndexRange,
kIndexRangeStart, kIndexRangeEnd, kMediaDuration, kFileSize};
return param;
}
void SetDefaultMuxerOptionsValues(MuxerOptions* muxer_options) {
muxer_options->single_segment = true;
muxer_options->segment_duration = 10.0;
muxer_options->fragment_duration = 10.0;
muxer_options->segment_sap_aligned = true;
muxer_options->fragment_sap_aligned = true;
muxer_options->num_subsegments_per_sidx = 0;
muxer_options->output_file_name = "test_output_file_name.mp4";
muxer_options->segment_template.clear();
muxer_options->temp_dir.clear();
}
void ExpectMediaInfoEqual(const MediaInfo& expect, const MediaInfo& actual) {
ASSERT_TRUE(MediaInfoEqual(expect, actual));
}
bool MediaInfoEqual(const MediaInfo& expect, const MediaInfo& actual) {
// I found out here
// https://groups.google.com/forum/#!msg/protobuf/5sOExQkB2eQ/ZSBNZI0K54YJ
// that the best way to check equality is to serialize and check equality.
std::string expect_serialized;
std::string actual_serialized;
EXPECT_TRUE(expect.SerializeToString(&expect_serialized));
EXPECT_TRUE(actual.SerializeToString(&actual_serialized));
EXPECT_EQ(expect_serialized, actual_serialized);
return expect_serialized == actual_serialized;
}
} // namespace media
} // namespace edash_packager

View File

@ -0,0 +1,103 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef MEDIA_EVENT_MUXER_LISTENER_TEST_HELPER_H_
#define MEDIA_EVENT_MUXER_LISTENER_TEST_HELPER_H_
#include <stdint.h>
#include <vector>
#include "packager/base/memory/ref_counted.h"
#include "packager/media/base/muxer_options.h"
#include "packager/media/base/stream_info.h"
#include "packager/media/base/video_stream_info.h"
#include "packager/mpd/base/media_info.pb.h"
namespace edash_packager {
namespace media {
const char kExpectedDefaultMediaInfo[] =
"bandwidth: 7620\n"
"video_info {\n"
" codec: 'avc1.010101'\n"
" width: 720\n"
" height: 480\n"
" time_scale: 10\n"
" pixel_width: 1\n"
" pixel_height: 1\n"
"}\n"
"init_range {\n"
" begin: 0\n"
" end: 120\n"
"}\n"
"index_range {\n"
" begin: 121\n"
" end: 221\n"
"}\n"
"reference_time_scale: 1000\n"
"container_type: 1\n"
"media_file_name: 'test_output_file_name.mp4'\n"
"media_duration_seconds: 10.5\n";
const uint32_t kDefaultReferenceTimeScale = 1000u;
// Struct that gets passed for to CreateVideoStreamInfo() to create a
// StreamInfo instance. Useful for generating multiple VideoStreamInfo with
// slightly different parameters.
struct VideoStreamInfoParameters {
VideoStreamInfoParameters();
~VideoStreamInfoParameters();
int track_id;
uint32_t time_scale;
uint64_t duration;
VideoCodec codec;
std::string codec_string;
std::string language;
uint16_t width;
uint16_t height;
uint32_t pixel_width;
uint32_t pixel_height;
uint8_t nalu_length_size;
std::vector<uint8_t> extra_data;
bool is_encrypted;
};
// Note that this does not have vector of StreamInfo pointer.
struct OnMediaEndParameters {
bool has_init_range;
uint64_t init_range_start;
uint64_t init_range_end;
bool has_index_range;
uint64_t index_range_start;
uint64_t index_range_end;
float duration_seconds;
uint64_t file_size;
};
// Creates StreamInfo instance from VideoStreamInfoParameters.
scoped_refptr<StreamInfo> CreateVideoStreamInfo(
const VideoStreamInfoParameters& param);
// Returns the "default" VideoStreamInfoParameters for testing.
VideoStreamInfoParameters GetDefaultVideoStreamInfoParams();
// Returns the "default" values for OnMediaEnd().
OnMediaEndParameters GetDefaultOnMediaEndParams();
// Sets "default" values for muxer_options for testing.
void SetDefaultMuxerOptionsValues(MuxerOptions* muxer_options);
// Expect that expect and actual are equal.
void ExpectMediaInfoEqual(const MediaInfo& expect, const MediaInfo& actual);
// Returns true if expect and actual are equal.
bool MediaInfoEqual(const MediaInfo& expect, const MediaInfo& actual);
} // namespace media
} // namespace edash_packager
#endif // MEDIA_EVENT_MUXER_LISTENER_TEST_HELPER_H_

View File

@ -11,11 +11,11 @@
#include "packager/base/file_util.h"
#include "packager/base/files/file_path.h"
#include "packager/base/stl_util.h"
#include "packager/media/event/vod_media_info_dump_muxer_listener.h"
#include "packager/media/file/file.h"
#include "packager/media/base/muxer_options.h"
#include "packager/media/base/video_stream_info.h"
#include "packager/media/event/muxer_listener_test_helper.h"
#include "packager/media/event/vod_media_info_dump_muxer_listener.h"
#include "packager/media/file/file.h"
#include "packager/mpd/base/media_info.pb.h"
namespace {
@ -39,119 +39,6 @@ namespace edash_packager {
namespace media {
namespace {
struct VideoStreamInfoParameters {
int track_id;
uint32_t time_scale;
uint64_t duration;
VideoCodec codec;
std::string codec_string;
std::string language;
uint16_t width;
uint16_t height;
uint32_t pixel_width;
uint32_t pixel_height;
uint8_t nalu_length_size;
std::vector<uint8_t> extra_data;
bool is_encrypted;
};
// Note that this does not have vector of StreamInfo pointer.
struct OnMediaEndParameters {
bool has_init_range;
uint64_t init_range_start;
uint64_t init_range_end;
bool has_index_range;
uint64_t index_range_start;
uint64_t index_range_end;
float duration_seconds;
uint64_t file_size;
};
scoped_refptr<StreamInfo> CreateVideoStreamInfo(
const VideoStreamInfoParameters& param) {
return scoped_refptr<StreamInfo>(
new VideoStreamInfo(param.track_id,
param.time_scale,
param.duration,
param.codec,
param.codec_string,
param.language,
param.width,
param.height,
param.pixel_width,
param.pixel_height,
0, // trick_play_rate
param.nalu_length_size,
vector_as_array(&param.extra_data),
param.extra_data.size(),
param.is_encrypted));
}
VideoStreamInfoParameters GetDefaultVideoStreamInfoParams() {
const int kTrackId = 0;
const uint32_t kTimeScale = 10;
const uint64_t kVideoStreamDuration = 200;
const VideoCodec kH264Codec = kCodecH264;
const uint8_t kH264Profile = 1;
const uint8_t kH264CompatibleProfile = 1;
const uint8_t kH264Level = 1;
const char* kLanuageUndefined = "und";
const uint16_t kWidth = 720;
const uint16_t kHeight = 480;
const uint32_t kPixelWidth = 1;
const uint32_t kPixelHeight = 1;
const uint8_t kNaluLengthSize = 1;
const std::vector<uint8_t> kExtraData;
const bool kEncryptedFlag = false;
VideoStreamInfoParameters param = {
kTrackId, kTimeScale, kVideoStreamDuration, kH264Codec,
VideoStreamInfo::GetCodecString(kCodecH264, kH264Profile,
kH264CompatibleProfile, kH264Level),
kLanuageUndefined, kWidth, kHeight, kPixelWidth, kPixelHeight,
kNaluLengthSize, kExtraData, kEncryptedFlag};
return param;
}
OnMediaEndParameters GetDefaultOnMediaEndParams() {
// Values for {init, index} range {start, end} are arbitrary, but makes sure
// that it is monotonically increasing and contiguous.
const bool kHasInitRange = true;
const uint64_t kInitRangeStart = 0;
const uint64_t kInitRangeEnd = kInitRangeStart + 120;
const uint64_t kHasIndexRange = true;
const uint64_t kIndexRangeStart = kInitRangeEnd + 1;
const uint64_t kIndexRangeEnd = kIndexRangeStart + 100;
const float kMediaDuration = 10.5f;
const uint64_t kFileSize = 10000;
OnMediaEndParameters param = {
kHasInitRange, kInitRangeStart, kInitRangeEnd, kHasIndexRange,
kIndexRangeStart, kIndexRangeEnd, kMediaDuration, kFileSize};
return param;
}
void SetDefaultMuxerOptionsValues(MuxerOptions* muxer_options) {
muxer_options->single_segment = true;
muxer_options->segment_duration = 10.0;
muxer_options->fragment_duration = 10.0;
muxer_options->segment_sap_aligned = true;
muxer_options->fragment_sap_aligned = true;
muxer_options->num_subsegments_per_sidx = 0;
muxer_options->output_file_name = "test_output_file_name.mp4";
muxer_options->segment_template.clear();
muxer_options->temp_dir.clear();
}
void ExpectMediaInfoEqual(const MediaInfo& expect, const MediaInfo& actual) {
// I found out here
// https://groups.google.com/forum/#!msg/protobuf/5sOExQkB2eQ/ZSBNZI0K54YJ
// that the best way to check equality is to serialize and check equality.
std::string expect_serialized;
std::string actual_serialized;
ASSERT_TRUE(expect.SerializeToString(&expect_serialized));
ASSERT_TRUE(actual.SerializeToString(&actual_serialized));
ASSERT_EQ(expect_serialized, actual_serialized);
}
void ExpectTextFormatMediaInfoEqual(const std::string& expect,
const std::string& actual) {
@ -167,6 +54,7 @@ void ExpectTextFormatMediaInfoEqual(const std::string& expect,
<< "Expect:" << std::endl << expect << std::endl
<< "Actual:" << std::endl << actual;
}
} // namespace
class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test {