From 787339f03a37a39c3a1dbdfffedd20372078f44e Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Thu, 25 Aug 2011 15:53:54 +0200 Subject: [PATCH] AVC/h.264: generate timecodes if provided timecodes are not enough --- src/common/mpeg4_p10.cpp | 27 ++++++++++++++++++++------- src/common/mpeg4_p10.h | 6 ++++++ src/input/r_mpeg_ts.cpp | 8 ++++++-- src/output/p_avc.cpp | 8 +++++++- src/output/p_avc.h | 1 + 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/common/mpeg4_p10.cpp b/src/common/mpeg4_p10.cpp index 5908b3705..6e4da8a81 100644 --- a/src/common/mpeg4_p10.cpp +++ b/src/common/mpeg4_p10.cpp @@ -569,6 +569,7 @@ mpeg4::p10::avc_es_parser_c::avc_es_parser_c() , m_first_keyframe_found(false) , m_recovery_point_valid(false) , m_b_frames_since_keyframe(false) + , m_max_timecode(0) , m_generate_timecodes(false) , m_have_incomplete_frame(false) , m_ignore_nalu_size_length_errors(false) @@ -684,6 +685,8 @@ mpeg4::p10::avc_es_parser_c::add_timecode(int64_t timecode) { --i; } m_timecodes.insert(i, timecode); + + m_max_timecode = std::max(timecode, m_max_timecode); } void @@ -1053,6 +1056,9 @@ mpeg4::p10::avc_es_parser_c::default_cleanup() { std::deque::iterator i(m_frames.begin()); std::deque::iterator t(m_timecodes.begin()); + if (m_frames.size() > m_timecodes.size()) + create_missing_timecodes(); + int64_t r = i->m_start = i->m_end = *t; ++i; @@ -1094,12 +1100,8 @@ mpeg4::p10::avc_es_parser_c::cleanup() { // (cluster_helper etc). i->m_keyframe = true; - if (m_timecodes.size() < m_frames.size()) { - mxverb(4, boost::format("mpeg4::p10::avc_es_parser_c::cleanup() numfr %1% sti %2%\n") % m_frames.size() % m_timecodes.size()); - m_timecodes.erase(m_timecodes.begin(), m_timecodes.begin() + m_frames.size()); - m_frames.clear(); - return; - } + if (m_frames.size() > m_timecodes.size()) + create_missing_timecodes(); slice_info_t &idr = i->m_si; sps_info_t &sps = m_sps_info_list[idr.sps]; @@ -1186,7 +1188,7 @@ mpeg4::p10::avc_es_parser_c::cleanup() { } m_frames_out.insert(m_frames_out.end(), m_frames.begin(), m_frames.end()); - m_timecodes.erase(m_timecodes.begin(), m_timecodes.begin() + m_frames.size()); + m_timecodes.erase(m_timecodes.begin(), m_timecodes.begin() + std::min(num_frames, num_timecodes)); m_frames.clear(); } @@ -1300,3 +1302,14 @@ mpeg4::p10::avc_es_parser_c::init_nalu_names() { m_nalu_names_by_type[NALU_TYPE_END_OF_STREAM] = "end of stream"; m_nalu_names_by_type[NALU_TYPE_FILLER_DATA] = "filler"; } + +void +mpeg4::p10::avc_es_parser_c::create_missing_timecodes() { + size_t num_timecodes = m_timecodes.size(); + size_t num_frames = m_frames.size(); + + while (num_timecodes < num_frames) { + ++num_timecodes; + add_timecode(m_max_timecode + m_default_duration); + } +} diff --git a/src/common/mpeg4_p10.h b/src/common/mpeg4_p10.h index 867654f66..c687593d7 100644 --- a/src/common/mpeg4_p10.h +++ b/src/common/mpeg4_p10.h @@ -196,6 +196,7 @@ namespace mpeg4 { std::deque m_frames, m_frames_out; std::deque m_timecodes; + int64_t m_max_timecode; bool m_generate_timecodes; @@ -219,6 +220,10 @@ namespace mpeg4 { avc_es_parser_c(); ~avc_es_parser_c(); + void set_default_duration(int64_t default_duration) { + m_default_duration = default_duration; + } + void enable_timecode_generation(int64_t default_duration) { m_default_duration = default_duration; m_generate_timecodes = true; @@ -308,6 +313,7 @@ namespace mpeg4 { void write_nalu_size(unsigned char *buffer, size_t size, int this_nalu_size_length = -1); memory_cptr create_nalu_with_size(const memory_cptr &src, bool add_extra_data = false); void init_nalu_names(); + void create_missing_timecodes(); }; typedef counted_ptr avc_es_parser_cptr; }; diff --git a/src/input/r_mpeg_ts.cpp b/src/input/r_mpeg_ts.cpp index 1351543ea..326ca048f 100644 --- a/src/input/r_mpeg_ts.cpp +++ b/src/input/r_mpeg_ts.cpp @@ -582,7 +582,7 @@ mpeg_ts_reader_c::new_stream_v_avc(unsigned char *buf, % sps_info.timing_info_present % sps_info.num_units_in_tick % sps_info.time_scale % sps_info.fixed_frame_rate); if (sps_info.timing_info_present && sps_info.num_units_in_tick) - track->v_frame_rate = sps_info.time_scale / sps_info.num_units_in_tick; + track->v_frame_rate = static_cast(sps_info.time_scale) / sps_info.num_units_in_tick; if (sps_info.ar_found) { float aspect_ratio = (float)sps_info.width / (float)sps_info.height * (float)sps_info.par_num / (float)sps_info.par_den; @@ -977,9 +977,13 @@ mpeg_ts_reader_c::create_mpeg4_p10_es_video_packetizer(mpeg_ts_track_ptr &track) mpeg4_p10_es_video_packetizer_c *avcpacketizer = new mpeg4_p10_es_video_packetizer_c(this, m_ti, track->v_avcc, track->v_width, track->v_height); track->ptzr = add_packetizer(avcpacketizer); - avcpacketizer->enable_timecode_generation(false); if (track->v_frame_rate) avcpacketizer->set_track_default_duration(static_cast(1000000000.0 / track->v_frame_rate)); + + // This is intentional so that the AVC parser knows the actual + // default duration from above but only generates timecode in + // emergencies. + avcpacketizer->enable_timecode_generation(false); } void diff --git a/src/output/p_avc.cpp b/src/output/p_avc.cpp index cfc5e1e50..567815f38 100644 --- a/src/output/p_avc.cpp +++ b/src/output/p_avc.cpp @@ -92,7 +92,7 @@ mpeg4_p10_es_video_packetizer_c::add_extra_data(memory_cptr data) { int mpeg4_p10_es_video_packetizer_c::process(packet_cptr packet) { try { - if (!m_allow_timecode_generation) + if (packet->has_timecode()) m_parser.add_timecode(packet->timecode); m_parser.add_bytes(packet->data->get_buffer(), packet->data->get_size()); flush_frames(); @@ -168,6 +168,12 @@ mpeg4_p10_es_video_packetizer_c::enable_timecode_generation(bool enable, } } +void +mpeg4_p10_es_video_packetizer_c::set_track_default_duration(int64_t default_duration) { + m_parser.set_default_duration(default_duration); + generic_packetizer_c::set_track_default_duration(default_duration); +} + void mpeg4_p10_es_video_packetizer_c::connect(generic_packetizer_c *src, int64_t p_append_timecode_offset) { diff --git a/src/output/p_avc.h b/src/output/p_avc.h index cd26e2e7d..bdaaf08a3 100644 --- a/src/output/p_avc.h +++ b/src/output/p_avc.h @@ -40,6 +40,7 @@ public: virtual void enable_timecode_generation(bool enable, int64_t default_duration = -1); virtual void extract_aspect_ratio(); + virtual void set_track_default_duration(int64_t default_duration); virtual const char *get_format_name() { return "AVC/h.264";