Add support to estimate file duration.

The code will estimate the file duration if the last block duration is
0. This is not totally correct, but better then what we currently have.

BUG=https://bugs.chromium.org/p/webm/issues/detail?id=1100

Change-Id: I8f81df0bd592e6f7b1925fa2637a2e09cf182742
This commit is contained in:
Frank Galligan 2016-10-13 12:57:54 -07:00
parent c97e3e7d60
commit 02bc809f9d
5 changed files with 75 additions and 3 deletions

View File

@ -3042,6 +3042,7 @@ Segment::Segment()
output_cues_(true),
accurate_cluster_duration_(false),
fixed_size_cluster_timecode_(false),
estimate_file_duration_(true),
payload_pos_(0),
size_position_(0),
doc_type_version_(kDefaultDocTypeVersion),
@ -3150,6 +3151,10 @@ bool Segment::Init(IMkvWriter* ptr_writer) {
writer_cluster_ = ptr_writer;
writer_cues_ = ptr_writer;
writer_header_ = ptr_writer;
memset(&track_frames_written_, 0,
sizeof(track_frames_written_[0]) * kMaxTrackNumber);
memset(&last_track_timestamp_, 0,
sizeof(last_track_timestamp_[0]) * kMaxTrackNumber);
return segment_info_.Init();
}
@ -3212,9 +3217,26 @@ bool Segment::Finalize() {
chunk_count_++;
}
const double duration =
double duration =
(static_cast<double>(last_timestamp_) + last_block_duration_) /
segment_info_.timecode_scale();
if (last_block_duration_ == 0 && estimate_file_duration_) {
const int num_tracks = static_cast<int>(tracks_.track_entries_size());
for (int i = 0; i < num_tracks; ++i) {
if (track_frames_written_[i] < 2)
continue;
// Estimate the duration for the last block of a Track.
const double nano_per_frame =
static_cast<double>(last_track_timestamp_[i]) /
(track_frames_written_[i] - 1);
const double track_duration =
(last_track_timestamp_[i] + nano_per_frame) /
segment_info_.timecode_scale();
if (track_duration > duration)
duration = track_duration;
}
}
segment_info_.set_duration(duration);
if (!segment_info_.Finalize(writer_header_))
return false;
@ -3460,7 +3482,10 @@ bool Segment::AddGenericFrame(const Frame* frame) {
Frame* const new_frame = new (std::nothrow) Frame();
if (!new_frame || !new_frame->CopyFrom(*frame))
return false;
return QueueFrame(new_frame);
if (!QueueFrame(new_frame))
return false;
track_frames_written_[frame->track_number() - 1]++;
return true;
}
if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(),
@ -3500,6 +3525,7 @@ bool Segment::AddGenericFrame(const Frame* frame) {
last_timestamp_ = frame->timestamp();
last_track_timestamp_[frame->track_number() - 1] = frame->timestamp();
last_block_duration_ = frame->duration();
track_frames_written_[frame->track_number() - 1]++;
if (frame_created)
delete frame;

View File

@ -1683,6 +1683,10 @@ class Segment {
Mode mode() const { return mode_; }
CuesPosition cues_position() const { return cues_position_; }
bool output_cues() const { return output_cues_; }
void set_estimate_file_duration(bool estimate_duration) {
estimate_file_duration_ = estimate_duration;
}
bool estimate_file_duration() const { return estimate_file_duration_; }
const SegmentInfo* segment_info() const { return &segment_info_; }
// Returns true when codec IDs are valid for WebM.
@ -1842,6 +1846,9 @@ class Segment {
// Last timestamp in nanoseconds by track number added to a cluster.
uint64_t last_track_timestamp_[kMaxTrackNumber];
// Number of frames written per track.
uint64_t track_frames_written_[kMaxTrackNumber];
// Maximum time in nanoseconds for a cluster duration. This variable is a
// guideline and some clusters may have a longer duration. Default is 30
// seconds.
@ -1870,6 +1877,9 @@ class Segment {
// Flag whether or not to write the Cluster Timecode using exactly 8 bytes.
bool fixed_size_cluster_timecode_;
// Flag whether or not to estimate the file duration.
bool estimate_file_duration_;
// The size of the EBML header, used to validate the header if
// WriteEbmlHeader() is called more than once.
int32_t ebml_header_size_;

View File

@ -192,6 +192,7 @@ TEST_F(MuxerTest, AddChapters) {
TEST_F(MuxerTest, SimpleBlock) {
EXPECT_TRUE(SegmentInit(false, false, false));
segment_.set_estimate_file_duration(false);
AddVideoTrack();
// Valid Frame
@ -220,6 +221,7 @@ TEST_F(MuxerTest, SimpleBlock) {
TEST_F(MuxerTest, SimpleBlockWithAddGenericFrame) {
EXPECT_TRUE(SegmentInit(false, false, false));
segment_.set_estimate_file_duration(false);
AddVideoTrack();
Frame frame;
@ -303,6 +305,7 @@ TEST_F(MuxerTest, TrackType) {
TEST_F(MuxerTest, BlockWithAdditional) {
EXPECT_TRUE(SegmentInit(false, false, false));
segment_.set_estimate_file_duration(false);
AddVideoTrack();
// Valid Frame
@ -344,6 +347,7 @@ TEST_F(MuxerTest, BlockWithAdditional) {
TEST_F(MuxerTest, BlockAdditionalWithAddGenericFrame) {
EXPECT_TRUE(SegmentInit(false, false, false));
segment_.set_estimate_file_duration(false);
AddVideoTrack();
Frame frame;
@ -407,6 +411,7 @@ TEST_F(MuxerTest, SegmentDurationComputation) {
TEST_F(MuxerTest, ForceNewCluster) {
EXPECT_TRUE(SegmentInit(false, false, false));
segment_.set_estimate_file_duration(false);
AddVideoTrack();
EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, 0,
@ -429,6 +434,7 @@ TEST_F(MuxerTest, ForceNewCluster) {
TEST_F(MuxerTest, OutputCues) {
EXPECT_TRUE(SegmentInit(true, false, false));
segment_.set_estimate_file_duration(false);
AddVideoTrack();
EXPECT_TRUE(
@ -449,6 +455,7 @@ TEST_F(MuxerTest, OutputCues) {
TEST_F(MuxerTest, CuesBeforeClusters) {
EXPECT_TRUE(SegmentInit(true, false, false));
segment_.set_estimate_file_duration(false);
AddVideoTrack();
EXPECT_TRUE(
@ -488,6 +495,7 @@ TEST_F(MuxerTest, CuesBeforeClusters) {
TEST_F(MuxerTest, MaxClusterSize) {
EXPECT_TRUE(SegmentInit(false, false, false));
segment_.set_estimate_file_duration(false);
AddVideoTrack();
const uint64_t kMaxClusterSize = 20;
segment_.set_max_cluster_size(kMaxClusterSize);
@ -513,6 +521,7 @@ TEST_F(MuxerTest, MaxClusterSize) {
TEST_F(MuxerTest, MaxClusterDuration) {
EXPECT_TRUE(SegmentInit(false, false, false));
segment_.set_estimate_file_duration(false);
AddVideoTrack();
const uint64_t kMaxClusterDuration = 4000000;
segment_.set_max_cluster_duration(kMaxClusterDuration);
@ -570,6 +579,7 @@ TEST_F(MuxerTest, SetCuesTrackNumber) {
TEST_F(MuxerTest, BlockWithDiscardPadding) {
EXPECT_TRUE(SegmentInit(false, false, false));
segment_.set_estimate_file_duration(false);
AddAudioTrack();
int timecode = 1000;
@ -593,6 +603,7 @@ TEST_F(MuxerTest, BlockWithDiscardPadding) {
TEST_F(MuxerTest, AccurateClusterDuration) {
EXPECT_TRUE(SegmentInit(false, true, false));
segment_.set_estimate_file_duration(false);
AddVideoTrack();
Frame frame;
@ -727,6 +738,7 @@ TEST_F(MuxerTest, AccurateClusterDurationWithoutFinalizingCluster) {
TEST_F(MuxerTest, UseFixedSizeClusterTimecode) {
EXPECT_TRUE(SegmentInit(false, false, true));
segment_.set_estimate_file_duration(false);
AddVideoTrack();
Frame frame;
@ -917,6 +929,29 @@ TEST_F(MuxerTest, Projection) {
EXPECT_TRUE(CompareFiles(GetTestFilePath("projection.webm"), filename_));
}
TEST_F(MuxerTest, EstimateDuration) {
EXPECT_TRUE(SegmentInit(false, false, false));
AddVideoTrack();
EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber, 0,
false));
EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber,
2000000, false));
EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber,
4000000, false));
EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber,
6000000, false));
EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber,
8000000, false));
EXPECT_TRUE(segment_.AddFrame(dummy_data_, kFrameLength, kVideoTrackNumber,
9000000, false));
segment_.Finalize();
CloseWriter();
EXPECT_TRUE(
CompareFiles(GetTestFilePath("estimate_duration.webm"), filename_));
}
} // namespace test
int main(int argc, char* argv[]) {

View File

@ -51,6 +51,7 @@ bool CompareFiles(const std::string& file1, const std::string& file2) {
const std::size_t r1 = std::fread(buf1, 1, kBlockSize, f1.get());
const std::size_t r2 = std::fread(buf2, 1, kBlockSize, f2.get());
// TODO(fgalligan): Add output of which byte differs.
if (r1 != r2 || std::memcmp(buf1, buf2, r1)) {
return 0; // Files are not equal
}
@ -211,4 +212,4 @@ bool ParseMkvFile(const std::string& webm_file) {
return result;
}
} // namespace test
} // namespace test

BIN
testing/testdata/estimate_duration.webm vendored Normal file

Binary file not shown.