Handle side data properly in webm

Change-Id: Ic8c81154ea66374df62a4a46d66c45b3035a7829
This commit is contained in:
KongQun Yang 2016-01-07 14:50:40 -08:00 committed by Gerrit Code Review
parent 58b95fd3d5
commit a040fb711c
6 changed files with 98 additions and 28 deletions

View File

@ -43,6 +43,9 @@ Status Fragmenter::AddSample(scoped_refptr<MediaSample> sample) {
return status; return status;
} }
if (sample->side_data_size() > 0)
LOG(WARNING) << "MP4 samples do not support side data. Side data ignored.";
// Fill in sample parameters. It will be optimized later. // Fill in sample parameters. It will be optimized later.
traf_->runs[0].sample_sizes.push_back(sample->data_size()); traf_->runs[0].sample_sizes.push_back(sample->data_size());
traf_->runs[0].sample_durations.push_back(sample->duration()); traf_->runs[0].sample_durations.push_back(sample->duration());

View File

@ -124,7 +124,8 @@ TEST_F(MultiSegmentSegmenterTest, BasicSupport) {
// Write the samples to the Segmenter. // Write the samples to the Segmenter.
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
scoped_refptr<MediaSample> sample = CreateSample(true, kDuration); scoped_refptr<MediaSample> sample =
CreateSample(kKeyFrame, kDuration, kNoSideData);
ASSERT_OK(segmenter_->AddSample(sample)); ASSERT_OK(segmenter_->AddSample(sample));
} }
ASSERT_OK(segmenter_->Finalize()); ASSERT_OK(segmenter_->Finalize());
@ -144,7 +145,8 @@ TEST_F(MultiSegmentSegmenterTest, SplitsFilesOnSegmentDuration) {
// Write the samples to the Segmenter. // Write the samples to the Segmenter.
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
scoped_refptr<MediaSample> sample = CreateSample(true, kDuration); scoped_refptr<MediaSample> sample =
CreateSample(kKeyFrame, kDuration, kNoSideData);
ASSERT_OK(segmenter_->AddSample(sample)); ASSERT_OK(segmenter_->AddSample(sample));
} }
ASSERT_OK(segmenter_->Finalize()); ASSERT_OK(segmenter_->Finalize());
@ -170,7 +172,9 @@ TEST_F(MultiSegmentSegmenterTest, RespectsSegmentSAPAlign) {
// Write the samples to the Segmenter. // Write the samples to the Segmenter.
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
scoped_refptr<MediaSample> sample = CreateSample(i == 6, kDuration); const KeyFrameFlag key_frame_flag = i == 6 ? kKeyFrame : kNotKeyFrame;
scoped_refptr<MediaSample> sample =
CreateSample(key_frame_flag, kDuration, kNoSideData);
ASSERT_OK(segmenter_->AddSample(sample)); ASSERT_OK(segmenter_->AddSample(sample));
} }
ASSERT_OK(segmenter_->Finalize()); ASSERT_OK(segmenter_->Finalize());
@ -195,7 +199,8 @@ TEST_F(MultiSegmentSegmenterTest, SplitsClustersOnFragmentDuration) {
// Write the samples to the Segmenter. // Write the samples to the Segmenter.
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
scoped_refptr<MediaSample> sample = CreateSample(true, kDuration); scoped_refptr<MediaSample> sample =
CreateSample(kKeyFrame, kDuration, kNoSideData);
ASSERT_OK(segmenter_->AddSample(sample)); ASSERT_OK(segmenter_->AddSample(sample));
} }
ASSERT_OK(segmenter_->Finalize()); ASSERT_OK(segmenter_->Finalize());
@ -218,7 +223,9 @@ TEST_F(MultiSegmentSegmenterTest, RespectsFragmentSAPAlign) {
// Write the samples to the Segmenter. // Write the samples to the Segmenter.
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
scoped_refptr<MediaSample> sample = CreateSample(i == 6, kDuration); const KeyFrameFlag key_frame_flag = i == 6 ? kKeyFrame : kNotKeyFrame;
scoped_refptr<MediaSample> sample =
CreateSample(key_frame_flag, kDuration, kNoSideData);
ASSERT_OK(segmenter_->AddSample(sample)); ASSERT_OK(segmenter_->AddSample(sample));
} }
ASSERT_OK(segmenter_->Finalize()); ASSERT_OK(segmenter_->Finalize());

View File

@ -122,11 +122,28 @@ Status Segmenter::AddSample(scoped_refptr<MediaSample> sample) {
const int64_t time_ns = const int64_t time_ns =
sample->pts() * kSecondsToNs / info_->time_scale(); sample->pts() * kSecondsToNs / info_->time_scale();
if (!cluster_->AddFrame(sample->data(), sample->data_size(), track_id_, bool addframe_result;
time_ns, sample->is_key_frame())) { if (sample->side_data_size() > 0) {
uint64_t block_add_id;
// First 8 bytes of side_data is the BlockAddID element's value, which is
// done to mimic ffmpeg behavior. See webm_cluster_parser.cc for details.
CHECK_GT(sample->side_data_size(), sizeof(block_add_id));
memcpy(&block_add_id, sample->side_data(), sizeof(block_add_id));
addframe_result = cluster_->AddFrameWithAdditional(
sample->data(), sample->data_size(),
sample->side_data() + sizeof(block_add_id),
sample->side_data_size() - sizeof(block_add_id), block_add_id,
track_id_, time_ns, sample->is_key_frame());
} else {
addframe_result =
cluster_->AddFrame(sample->data(), sample->data_size(), track_id_,
time_ns, sample->is_key_frame());
}
if (!addframe_result) {
LOG(ERROR) << "Error adding sample to segment."; LOG(ERROR) << "Error adding sample to segment.";
return Status(error::FILE_FAILURE, "Error adding sample to segment."); return Status(error::FILE_FAILURE, "Error adding sample to segment.");
} }
const double duration_sec = const double duration_sec =
static_cast<double>(sample->duration()) / info_->time_scale(); static_cast<double>(sample->duration()) / info_->time_scale();
cluster_length_sec_ += duration_sec; cluster_length_sec_ += duration_sec;

View File

@ -15,8 +15,11 @@ namespace media {
namespace { namespace {
// The contents of a frame does not mater. // The contents of a frame does not mater.
const uint8_t kTestMediaSampleData[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00}; const uint8_t kTestMediaSampleData[] = {0xde, 0xad, 0xbe, 0xef, 0x00};
const size_t kTestMediaSampleDataSize = sizeof(kTestMediaSampleData); const uint8_t kTestMediaSampleSideData[] = {
// First 8 bytes of side_data is the BlockAddID element in big endian.
0x12, 0x34, 0x56, 0x78, 0x9a, 0x00, 0x00, 0x00,
0x73, 0x69, 0x64, 0x65, 0x00};
const int kTrackId = 1; const int kTrackId = 1;
const uint32_t kTimeScale = 1000; const uint32_t kTimeScale = 1000;
@ -46,10 +49,21 @@ void SegmentTestBase::TearDown() {
MemoryFile::DeleteAll(); MemoryFile::DeleteAll();
} }
scoped_refptr<MediaSample> SegmentTestBase::CreateSample(bool is_key_frame, scoped_refptr<MediaSample> SegmentTestBase::CreateSample(
uint64_t duration) { KeyFrameFlag key_frame_flag,
scoped_refptr<MediaSample> sample = MediaSample::CopyFrom( uint64_t duration,
kTestMediaSampleData, kTestMediaSampleDataSize, is_key_frame); SideDataFlag side_data_flag) {
scoped_refptr<MediaSample> sample;
const bool is_key_frame = key_frame_flag == kKeyFrame;
if (side_data_flag == kGenerateSideData) {
sample = MediaSample::CopyFrom(
kTestMediaSampleData, sizeof(kTestMediaSampleData),
kTestMediaSampleSideData, sizeof(kTestMediaSampleSideData),
is_key_frame);
} else {
sample = MediaSample::CopyFrom(kTestMediaSampleData,
sizeof(kTestMediaSampleData), is_key_frame);
}
sample->set_dts(cur_time_timescale_); sample->set_dts(cur_time_timescale_);
sample->set_pts(cur_time_timescale_); sample->set_pts(cur_time_timescale_);
sample->set_duration(duration); sample->set_duration(duration);
@ -170,8 +184,8 @@ bool SegmentTestBase::ClusterParser::OnFloat(int id, double val) {
bool SegmentTestBase::ClusterParser::OnBinary(int id, bool SegmentTestBase::ClusterParser::OnBinary(int id,
const uint8_t* data, const uint8_t* data,
int size) { int size) {
if (in_cluster_ && id == kWebMIdSimpleBlock) { if (in_cluster_ && (id == kWebMIdSimpleBlock || id == kWebMIdBlock)) {
cluster_sizes_[cluster_sizes_.size() - 1]++; cluster_sizes_.back()++;
} }
return true; return true;

View File

@ -26,6 +26,16 @@ namespace edash_packager {
namespace media { namespace media {
class SegmentTestBase : public ::testing::Test { class SegmentTestBase : public ::testing::Test {
public:
enum KeyFrameFlag {
kKeyFrame,
kNotKeyFrame,
};
enum SideDataFlag {
kGenerateSideData,
kNoSideData,
};
protected: protected:
SegmentTestBase(); SegmentTestBase();
@ -46,7 +56,9 @@ class SegmentTestBase : public ::testing::Test {
} }
/// Creates a new media sample. /// Creates a new media sample.
scoped_refptr<MediaSample> CreateSample(bool is_key_frame, uint64_t duration); scoped_refptr<MediaSample> CreateSample(KeyFrameFlag key_frame_flag,
uint64_t duration,
SideDataFlag side_data_flag);
/// Creates a Muxer options object for testing. /// Creates a Muxer options object for testing.
MuxerOptions CreateMuxerOptions() const; MuxerOptions CreateMuxerOptions() const;
/// Creates a video stream info object for testing. /// Creates a video stream info object for testing.

View File

@ -33,8 +33,8 @@ const uint8_t kBasicSupportData[] = {
0x42, 0x87, 0x81, 0x02, 0x42, 0x87, 0x81, 0x02,
// DocTypeReadVersion: 2 // DocTypeReadVersion: 2
0x42, 0x85, 0x81, 0x02, 0x42, 0x85, 0x81, 0x02,
// ID: Segment, Payload Size: 316 // ID: Segment, Payload Size: 337
0x18, 0x53, 0x80, 0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x18, 0x53, 0x80, 0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x51,
// ID: SeekHead, Payload Size: 30 // ID: SeekHead, Payload Size: 30
0x11, 0x4d, 0x9b, 0x74, 0x9e, 0x11, 0x4d, 0x9b, 0x74, 0x9e,
// ID: Seek, Payload Size: 12 // ID: Seek, Payload Size: 12
@ -47,8 +47,8 @@ const uint8_t kBasicSupportData[] = {
0x4d, 0xbb, 0x8c, 0x4d, 0xbb, 0x8c,
// SeekID: binary(4) (Cues) // SeekID: binary(4) (Cues)
0x53, 0xab, 0x84, 0x1c, 0x53, 0xbb, 0x6b, 0x53, 0xab, 0x84, 0x1c, 0x53, 0xbb, 0x6b,
// SeekPosition: 346 // SeekPosition: 367
0x53, 0xac, 0x82, 0x01, 0x5a, 0x53, 0xac, 0x82, 0x01, 0x6f,
// ID: Void, Payload Size: 52 // ID: Void, Payload Size: 52
0xec, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -95,8 +95,8 @@ const uint8_t kBasicSupportData[] = {
0x54, 0xb0, 0x81, 0x64, 0x54, 0xb0, 0x81, 0x64,
// DisplayHeight: 100 // DisplayHeight: 100
0x54, 0xba, 0x81, 0x64, 0x54, 0xba, 0x81, 0x64,
// ID: Cluster, Payload Size: 58 // ID: Cluster, Payload Size: 79
0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f,
// Timecode: 0 // Timecode: 0
0xe7, 0x81, 0x00, 0xe7, 0x81, 0x00,
// ID: SimpleBlock, Payload Size: 9 // ID: SimpleBlock, Payload Size: 9
@ -105,8 +105,18 @@ const uint8_t kBasicSupportData[] = {
0xa3, 0x89, 0x81, 0x03, 0xe8, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xa3, 0x89, 0x81, 0x03, 0xe8, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00,
// ID: SimpleBlock, Payload Size: 9 // ID: SimpleBlock, Payload Size: 9
0xa3, 0x89, 0x81, 0x07, 0xd0, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xa3, 0x89, 0x81, 0x07, 0xd0, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00,
// ID: SimpleBlock, Payload Size: 9 // ID: BlockGroup, Payload Size: 30
0xa3, 0x89, 0x81, 0x0b, 0xb8, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xa0, 0x9e,
// ID: Block, Payload Size: 9
0xa1, 0x89, 0x81, 0x0b, 0xb8, 0x00, 0xde, 0xad, 0xbe, 0xef, 0x00,
// ID: BlockAdditions, Payload Size: 16
0x75, 0xa1, 0x90,
// ID: BlockMore, Payload Size: 14
0xa6, 0x8e,
// ID: BlockAddID, Payload Size: 1
0xee, 0x85, 0x9a, 0x78, 0x56, 0x34, 0x12,
// ID: BlockAdditional, Payload Size: 5
0xa5, 0x85, 0x73, 0x69, 0x64, 0x65, 0x00,
// ID: SimpleBlock, Payload Size: 9 // ID: SimpleBlock, Payload Size: 9
0xa3, 0x89, 0x81, 0x0f, 0xa0, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xa3, 0x89, 0x81, 0x0f, 0xa0, 0x80, 0xde, 0xad, 0xbe, 0xef, 0x00,
// ID: Cues, Payload Size: 13 // ID: Cues, Payload Size: 13
@ -156,7 +166,10 @@ TEST_P(SingleSegmentSegmenterTest, BasicSupport) {
// Write the samples to the Segmenter. // Write the samples to the Segmenter.
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
scoped_refptr<MediaSample> sample = CreateSample(true, kDuration); const SideDataFlag side_data_flag =
i == 3 ? kGenerateSideData : kNoSideData;
scoped_refptr<MediaSample> sample =
CreateSample(kKeyFrame, kDuration, side_data_flag);
ASSERT_OK(segmenter_->AddSample(sample)); ASSERT_OK(segmenter_->AddSample(sample));
} }
ASSERT_OK(segmenter_->Finalize()); ASSERT_OK(segmenter_->Finalize());
@ -171,7 +184,8 @@ TEST_P(SingleSegmentSegmenterTest, SplitsClustersOnSegmentDuration) {
// Write the samples to the Segmenter. // Write the samples to the Segmenter.
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
scoped_refptr<MediaSample> sample = CreateSample(true, kDuration); scoped_refptr<MediaSample> sample =
CreateSample(kKeyFrame, kDuration, kNoSideData);
ASSERT_OK(segmenter_->AddSample(sample)); ASSERT_OK(segmenter_->AddSample(sample));
} }
ASSERT_OK(segmenter_->Finalize()); ASSERT_OK(segmenter_->Finalize());
@ -191,7 +205,8 @@ TEST_P(SingleSegmentSegmenterTest, IgnoresFragmentDuration) {
// Write the samples to the Segmenter. // Write the samples to the Segmenter.
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
scoped_refptr<MediaSample> sample = CreateSample(true, kDuration); scoped_refptr<MediaSample> sample =
CreateSample(kKeyFrame, kDuration, kNoSideData);
ASSERT_OK(segmenter_->AddSample(sample)); ASSERT_OK(segmenter_->AddSample(sample));
} }
ASSERT_OK(segmenter_->Finalize()); ASSERT_OK(segmenter_->Finalize());
@ -211,7 +226,9 @@ TEST_P(SingleSegmentSegmenterTest, RespectsSAPAlign) {
// Write the samples to the Segmenter. // Write the samples to the Segmenter.
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
scoped_refptr<MediaSample> sample = CreateSample(i == 6, kDuration); const KeyFrameFlag key_frame_flag = i == 6 ? kKeyFrame : kNotKeyFrame;
scoped_refptr<MediaSample> sample =
CreateSample(key_frame_flag, kDuration, kNoSideData);
ASSERT_OK(segmenter_->AddSample(sample)); ASSERT_OK(segmenter_->AddSample(sample));
} }
ASSERT_OK(segmenter_->Finalize()); ASSERT_OK(segmenter_->Finalize());