diff --git a/mkvmuxer/mkvmuxer.cc b/mkvmuxer/mkvmuxer.cc index 12ac8f7..f48d282 100644 --- a/mkvmuxer/mkvmuxer.cc +++ b/mkvmuxer/mkvmuxer.cc @@ -3047,6 +3047,7 @@ Segment::Segment() size_position_(0), doc_type_version_(kDefaultDocTypeVersion), doc_type_version_written_(0), + duration_(0.0), writer_cluster_(NULL), writer_cues_(NULL), writer_header_(NULL) { @@ -3220,21 +3221,25 @@ bool Segment::Finalize() { double duration = (static_cast(last_timestamp_) + last_block_duration_) / segment_info_.timecode_scale(); - if (last_block_duration_ == 0 && estimate_file_duration_) { - const int num_tracks = static_cast(tracks_.track_entries_size()); - for (int i = 0; i < num_tracks; ++i) { - if (track_frames_written_[i] < 2) - continue; + if (duration_ > 0.0) { + duration = duration_; + } else { + if (last_block_duration_ == 0 && estimate_file_duration_) { + const int num_tracks = static_cast(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(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; + // Estimate the duration for the last block of a Track. + const double nano_per_frame = + static_cast(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); diff --git a/mkvmuxer/mkvmuxer.h b/mkvmuxer/mkvmuxer.h index f7a1ced..5868266 100644 --- a/mkvmuxer/mkvmuxer.h +++ b/mkvmuxer/mkvmuxer.h @@ -1688,6 +1688,8 @@ class Segment { } bool estimate_file_duration() const { return estimate_file_duration_; } const SegmentInfo* segment_info() const { return &segment_info_; } + void set_duration(double duration) { duration_ = duration; } + double duration() const { return duration_; } // Returns true when codec IDs are valid for WebM. bool DocTypeIsWebm() const; @@ -1897,6 +1899,9 @@ class Segment { uint32_t doc_type_version_; uint32_t doc_type_version_written_; + // If |duration_| is > 0, then explicitly set the duration of the segment. + double duration_; + // Pointer to the writer objects. Not owned by this class. IMkvWriter* writer_cluster_; IMkvWriter* writer_cues_; diff --git a/mkvmuxer_sample.cc b/mkvmuxer_sample.cc index 813cb0c..473b05a 100644 --- a/mkvmuxer_sample.cc +++ b/mkvmuxer_sample.cc @@ -54,6 +54,7 @@ void Usage() { printf(">0 Writes the last frame in each cluster with Duration\n"); printf(" -fixed_size_cluster_timecode "); printf(">0 Writes the cluster timecode using exactly 8 bytes\n"); + printf(" -copy_input_duration >0 Copies the input duration\n"); printf("\n"); printf("Video options:\n"); printf(" -display_width Display width in pixels\n"); @@ -170,6 +171,7 @@ int main(int argc, char* argv[]) { const char* chunk_name = NULL; bool accurate_cluster_duration = false; bool fixed_size_cluster_timecode = false; + bool copy_input_duration = false; bool output_cues_block_number = true; @@ -234,6 +236,8 @@ int main(int argc, char* argv[]) { i < argc_check) { fixed_size_cluster_timecode = strtol(argv[++i], &end, 10) == 0 ? false : true; + } else if (!strcmp("-copy_input_duration", argv[i]) && i < argc_check) { + copy_input_duration = strtol(argv[++i], &end, 10) == 0 ? false : true; } else if (!strcmp("-display_width", argv[i]) && i < argc_check) { display_width = strtol(argv[++i], &end, 10); } else if (!strcmp("-display_height", argv[i]) && i < argc_check) { @@ -655,6 +659,12 @@ int main(int argc, char* argv[]) { if (!metadata.Write(-1)) return EXIT_FAILURE; + if (copy_input_duration) { + const double input_duration = + static_cast(segment_info->GetDuration()) / timeCodeScale; + muxer_segment.set_duration(input_duration); + } + if (!muxer_segment.Finalize()) { printf("Finalization of segment failed.\n"); return EXIT_FAILURE; diff --git a/testing/mkvmuxer_tests.cc b/testing/mkvmuxer_tests.cc index 6826ba4..68e279d 100644 --- a/testing/mkvmuxer_tests.cc +++ b/testing/mkvmuxer_tests.cc @@ -409,6 +409,22 @@ TEST_F(MuxerTest, SegmentDurationComputation) { CompareFiles(GetTestFilePath("segment_duration.webm"), filename_)); } +TEST_F(MuxerTest, SetSegmentDuration) { + 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)); + + segment_.set_duration(10500.0); + segment_.Finalize(); + CloseWriter(); + + EXPECT_TRUE( + CompareFiles(GetTestFilePath("set_segment_duration.webm"), filename_)); +} + TEST_F(MuxerTest, ForceNewCluster) { EXPECT_TRUE(SegmentInit(false, false, false)); segment_.set_estimate_file_duration(false); diff --git a/testing/testdata/set_segment_duration.webm b/testing/testdata/set_segment_duration.webm new file mode 100644 index 0000000..100684b Binary files /dev/null and b/testing/testdata/set_segment_duration.webm differ