bool cluster_helper_c::must_duration_be_set(render_groups_c *rg, packet_cptr &new_packet) { size_t i; int64_t block_duration = 0; int64_t def_duration = rg->m_source->get_track_default_duration(); for (i = 0; rg->m_durations.size() > i; ++i) block_duration += rg->m_durations[i]; block_duration += new_packet->get_duration(); if (rg->m_duration_mandatory || new_packet->duration_mandatory) { if ( (0 == block_duration) || ( (0 < block_duration) && (block_duration != ((static_cast<int64_t>(rg->m_durations.size()) + 1) * def_duration)))) return true; } else if ( ( g_use_durations || (0 < def_duration)) && (0 < block_duration) && (RND_TIMECODE_SCALE(block_duration) != RND_TIMECODE_SCALE((rg->m_durations.size() + 1) * def_duration))) return true; return false; }
bool cluster_helper_c::add_to_cues_maybe(packet_cptr &pack) { auto &source = *pack->source; auto strategy = source.get_cue_creation(); // Update the cues (index table) either if cue entries for I frames were requested and this is an I frame... bool add = (CUE_STRATEGY_IFRAMES == strategy) && pack->is_key_frame(); // ... or if a codec state change is present ... add = add || !!pack->codec_state; // ... or if the user requested entries for all frames ... add = add || (CUE_STRATEGY_ALL == strategy); // ... or if this is an audio track, there is no video track and the // last cue entry was created more than 2s ago. add = add || ( (CUE_STRATEGY_SPARSE == strategy) && (track_audio == source.get_track_type()) && !g_video_packetizer && ( (0 > source.get_last_cue_timecode()) || ((pack->assigned_timecode - source.get_last_cue_timecode()) >= 2000000000))); if (!add) return false; source.set_last_cue_timecode(pack->assigned_timecode); ++m->num_cue_elements; g_cue_writing_requested = 1; return true; }
int mpeg1_2_video_packetizer_c::process(packet_cptr packet) { if (0.0 > m_fps) extract_fps(packet->data->get_buffer(), packet->data->get_size()); if (!m_aspect_ratio_extracted) extract_aspect_ratio(packet->data->get_buffer(), packet->data->get_size()); if (m_framed) return process_framed(packet); int state = m_parser.GetState(); if ((MPV_PARSER_STATE_EOS == state) || (MPV_PARSER_STATE_ERROR == state)) return FILE_STATUS_DONE; memory_cptr old_memory = packet->data; unsigned char *data_ptr = old_memory->get_buffer(); int new_bytes = old_memory->get_size(); if (packet->has_timecode()) m_parser.AddTimecode(packet->timecode); do { int bytes_to_add = (m_parser.GetFreeBufferSpace() < new_bytes) ? m_parser.GetFreeBufferSpace() : new_bytes; if (0 < bytes_to_add) { m_parser.WriteData(data_ptr, bytes_to_add); data_ptr += bytes_to_add; new_bytes -= bytes_to_add; } state = m_parser.GetState(); while (MPV_PARSER_STATE_FRAME == state) { MPEGFrame *frame = m_parser.ReadFrame(); if (NULL == frame) break; if (NULL == m_hcodec_private) create_private_data(); packet_cptr new_packet = packet_cptr(new packet_t(new memory_c(frame->data, frame->size, true), frame->timecode, frame->duration, frame->firstRef, frame->secondRef)); new_packet->time_factor = MPEG2_PICTURE_TYPE_FRAME == frame->pictureStructure ? 1 : 2; put_sequence_headers_into_codec_state(new_packet); video_packetizer_c::process(new_packet); frame->data = NULL; delete frame; state = m_parser.GetState(); } } while (0 < new_bytes); return FILE_STATUS_MOREDATA; }
int hdmv_textst_packetizer_c::process(packet_cptr packet) { if ((packet->data->get_size() < 13) || (static_cast<mtx::hdmv_textst::segment_type_e>(packet->data->get_buffer()[0]) != mtx::hdmv_textst::dialog_presentation_segment)) return FILE_STATUS_MOREDATA; auto buf = packet->data->get_buffer(); auto start_pts = timestamp_c::mpeg((static_cast<int64_t>(buf[3] & 1) << 32) | get_uint32_be(&buf[4])); auto end_pts = timestamp_c::mpeg((static_cast<int64_t>(buf[8] & 1) << 32) | get_uint32_be(&buf[9])); if (!packet->has_timestamp()) packet->timestamp = start_pts.to_ns(); if (!packet->has_duration()) packet->duration = (end_pts - start_pts).abs().to_ns(); packet->duration_mandatory = true; add_packet(packet); return FILE_STATUS_MOREDATA; }
void cluster_helper_c::split_if_necessary(packet_cptr &packet) { if ( !splitting() || (m->split_points.end() == m->current_split_point) || (g_file_num > g_split_max_num_files) || !packet->is_key_frame() || ( (packet->source->get_track_type() != track_video) && g_video_packetizer)) return; bool split_now = false; // Maybe we want to start a new file now. if (split_point_c::size == m->current_split_point->m_type) { int64_t additional_size = 0; if (!m->packets.empty()) // Cluster + Cluster timecode: roughly 21 bytes. Add all frame sizes & their overheaders, too. additional_size = 21 + boost::accumulate(m->packets, 0, [](size_t size, const packet_cptr &p) { return size + p->data->get_size() + (p->is_key_frame() ? 10 : p->is_p_frame() ? 13 : 16); }); additional_size += 18 * m->num_cue_elements; mxdebug_if(m->debug_splitting, boost::format("cluster_helper split decision: header_overhead: %1%, additional_size: %2%, bytes_in_file: %3%, sum: %4%\n") % m->header_overhead % additional_size % m->bytes_in_file % (m->header_overhead + additional_size + m->bytes_in_file)); if ((m->header_overhead + additional_size + m->bytes_in_file) >= m->current_split_point->m_point) split_now = true; } else if ( (split_point_c::duration == m->current_split_point->m_type) && (0 <= m->first_timecode_in_file) && (packet->assigned_timecode - m->first_timecode_in_file) >= m->current_split_point->m_point) split_now = true; else if ( ( (split_point_c::timecode == m->current_split_point->m_type) || (split_point_c::parts == m->current_split_point->m_type)) && (packet->assigned_timecode >= m->current_split_point->m_point)) split_now = true; else if ( ( (split_point_c::frame_field == m->current_split_point->m_type) || (split_point_c::parts_frame_field == m->current_split_point->m_type)) && (m->frame_field_number >= m->current_split_point->m_point)) split_now = true; if (!split_now) return; split(packet); }
int pcm_packetizer_c::process(packet_cptr packet) { if (packet->has_timecode() && (packet->data->get_size() >= m_min_packet_size)) return process_packaged(packet); m_buffer.add(packet->data->get_buffer(), packet->data->get_size()); while (m_buffer.get_size() >= m_packet_size) { add_packet(new packet_t(memory_c::clone(m_buffer.get_buffer(), m_packet_size), m_samples_output * m_s2tc, m_samples_per_packet * m_s2tc)); m_buffer.remove(m_packet_size); m_samples_output += m_samples_per_packet; } return FILE_STATUS_MOREDATA; }
int mpeg1_2_video_packetizer_c::process_unframed(packet_cptr packet) { int state = m_parser.GetState(); if ((MPV_PARSER_STATE_EOS == state) || (MPV_PARSER_STATE_ERROR == state)) return FILE_STATUS_DONE; auto old_memory = packet->data; auto data_ptr = old_memory->get_buffer(); int new_bytes = old_memory->get_size(); if (packet->has_timecode()) m_parser.AddTimecode(packet->timecode); do { int bytes_to_add = std::min<int>(m_parser.GetFreeBufferSpace(), new_bytes); if (0 < bytes_to_add) { m_parser.WriteData(data_ptr, bytes_to_add); data_ptr += bytes_to_add; new_bytes -= bytes_to_add; } state = m_parser.GetState(); while (MPV_PARSER_STATE_FRAME == state) { auto frame = std::shared_ptr<MPEGFrame>(m_parser.ReadFrame()); if (!frame) break; if (!m_hcodec_private) create_private_data(); packet_cptr new_packet = packet_cptr(new packet_t(new memory_c(frame->data, frame->size, true), frame->timecode, frame->duration, frame->refs[0], frame->refs[1])); new_packet->time_factor = MPEG2_PICTURE_TYPE_FRAME == frame->pictureStructure ? 1 : 2; remove_stuffing_bytes_and_handle_sequence_headers(new_packet); generic_video_packetizer_c::process(new_packet); frame->data = nullptr; state = m_parser.GetState(); } } while (0 < new_bytes); return FILE_STATUS_MOREDATA; }
void cluster_helper_c::render_before_adding_if_necessary(packet_cptr &packet) { int64_t timecode = get_timecode(); int64_t timecode_delay = ( (packet->assigned_timecode > m->max_timecode_in_cluster) || (-1 == m->max_timecode_in_cluster)) ? packet->assigned_timecode : m->max_timecode_in_cluster; timecode_delay -= ( (-1 == m->min_timecode_in_cluster) || (packet->assigned_timecode < m->min_timecode_in_cluster)) ? packet->assigned_timecode : m->min_timecode_in_cluster; timecode_delay = (int64_t)(timecode_delay / g_timecode_scale); mxdebug_if(m->debug_packets, boost::format("cluster_helper_c::add_packet(): new packet { source %1%/%2% " "timecode: %3% duration: %4% bref: %5% fref: %6% assigned_timecode: %7% timecode_delay: %8% }\n") % packet->source->m_ti.m_id % packet->source->m_ti.m_fname % packet->timecode % packet->duration % packet->bref % packet->fref % packet->assigned_timecode % format_timecode(timecode_delay)); bool is_video_keyframe = (packet->source == g_video_packetizer) && packet->is_key_frame(); bool do_render = (std::numeric_limits<int16_t>::max() < timecode_delay) || (std::numeric_limits<int16_t>::min() > timecode_delay) || ( (std::max<int64_t>(0, m->min_timecode_in_cluster) > m->previous_cluster_tc) && (packet->assigned_timecode > m->min_timecode_in_cluster) && (!g_video_packetizer || !is_video_keyframe || m->first_video_keyframe_seen) && ( (packet->gap_following && !m->packets.empty()) || ((packet->assigned_timecode - timecode) > g_max_ns_per_cluster) || is_video_keyframe)); if (is_video_keyframe) m->first_video_keyframe_seen = true; mxdebug_if(m->debug_rendering, boost::format("render check cur_tc %9% min_tc_ic %1% prev_cl_tc %2% test %3% is_vid_and_key %4% tc_delay %5% gap_following_and_not_empty %6% cur_tc>min_tc_ic %8% first_video_key_seen %10% do_render %7%\n") % m->min_timecode_in_cluster % m->previous_cluster_tc % (std::max<int64_t>(0, m->min_timecode_in_cluster) > m->previous_cluster_tc) % is_video_keyframe % timecode_delay % (packet->gap_following && !m->packets.empty()) % do_render % (packet->assigned_timecode > m->min_timecode_in_cluster) % packet->assigned_timecode % m->first_video_keyframe_seen); if (!do_render) return; render(); prepare_new_cluster(); }
int opus_packetizer_c::process(packet_cptr packet) { try { auto toc = mtx::opus::toc_t::decode(packet->data); mxdebug_if(m_debug, boost::format("TOC: %1%\n") % toc); if (!packet->has_timecode() || (timecode_c::ns(packet->timecode) == m_previous_provided_timecode)) packet->timecode = m_next_calculated_timecode.to_ns(); else m_previous_provided_timecode = timecode_c::ns(packet->timecode); packet->duration = toc.packet_duration.to_ns(); m_next_calculated_timecode = timecode_c::ns(packet->timecode + packet->duration); add_packet(packet); } catch (mtx::opus::exception &ex) { mxdebug_if(m_debug, boost::format("Exception: %1%\n") % ex.what()); } return FILE_STATUS_MOREDATA; }
void cluster_helper_c::add_packet(packet_cptr packet) { if (!m->cluster) prepare_new_cluster(); packet->normalize_timecodes(); render_before_adding_if_necessary(packet); split_if_necessary(packet); m->packets.push_back(packet); m->cluster_content_size += packet->data->get_size(); if (packet->assigned_timecode > m->max_timecode_in_cluster) m->max_timecode_in_cluster = packet->assigned_timecode; if ((-1 == m->min_timecode_in_cluster) || (packet->assigned_timecode < m->min_timecode_in_cluster)) m->min_timecode_in_cluster = packet->assigned_timecode; render_after_adding_if_necessary(packet); if (g_video_packetizer == packet->source) ++m->frame_field_number; }
int mpeg4_p10_es_video_packetizer_c::process(packet_cptr packet) { try { if (packet->has_timecode()) m_parser.add_timecode(packet->timecode); m_parser.add_bytes(packet->data->get_buffer(), packet->data->get_size()); flush_frames(); } catch (nalu_size_length_x &error) { mxerror_tid(m_ti.m_fname, m_ti.m_id, boost::format(Y("This AVC/h.264 contains frames that are too big for the current maximum NALU size. " "You have to re-run mkvmerge and set the maximum NALU size to %1% for this track " "(command line parameter '--nalu-size-length %2%:%1%').\n")) % error.get_required_length() % m_ti.m_id); } catch (mtx::exception &error) { mxerror_tid(m_ti.m_fname, m_ti.m_id, boost::format(Y("mkvmerge encountered broken or unparsable data in this AVC/h.264 video track. " "Either your file is damaged (which mkvmerge cannot cope with yet) or this is a bug in mkvmerge itself. " "The error message was:\n%1%\n")) % error.error()); } return FILE_STATUS_MOREDATA; }
int textsubs_packetizer_c::process(packet_cptr packet) { ++m_packetno; if (0 > packet->duration) { subtitle_number_packet_extension_c *extension = dynamic_cast<subtitle_number_packet_extension_c *>(packet->find_extension(packet_extension_c::SUBTITLE_NUMBER)); mxwarn_tid(m_ti.m_fname, m_ti.m_id, boost::format(Y("Ignoring an entry which starts after it ends (%1%).\n")) % (extension ? extension->get_number() : static_cast<unsigned int>(m_packetno))); return FILE_STATUS_MOREDATA; } packet->duration_mandatory = true; std::string subs((char *)packet->data->get_buffer()); subs = boost::regex_replace(subs, s_re_remove_cr, "", boost::match_default | boost::match_single_line); subs = boost::regex_replace(subs, s_re_remove_trailing_nl, "", boost::match_default | boost::match_single_line); subs = boost::regex_replace(subs, s_re_translate_nl, "\r\n", boost::match_default | boost::match_single_line); if (m_recode) subs = m_cc_utf8->utf8(subs); packet->data = memory_cptr(new memory_c((unsigned char *)subs.c_str(), subs.length(), false)); add_packet(packet); return FILE_STATUS_MOREDATA; }
void generic_packetizer_c::add_packet2(packet_cptr pack) { if (pack->has_discard_padding()) set_required_matroska_version(4); pack->timecode = ADJUST_TIMECODE(pack->timecode); if (pack->has_bref()) pack->bref = ADJUST_TIMECODE(pack->bref); if (pack->has_fref()) pack->fref = ADJUST_TIMECODE(pack->fref); if (pack->has_duration()) { pack->duration = static_cast<int64_t>(pack->duration * m_ti.m_tcsync.numerator / m_ti.m_tcsync.denominator); if (pack->has_discard_padding()) pack->duration -= std::min(pack->duration, pack->discard_padding.to_ns()); } if ((2 > m_htrack_min_cache) && pack->has_fref()) { set_track_min_cache(2); rerender_track_headers(); } else if ((1 > m_htrack_min_cache) && pack->has_bref()) { set_track_min_cache(1); rerender_track_headers(); } if (0 > pack->timecode) return; // 'timecode < safety_last_timecode' may only occur for B frames. In this // case we have the coding order, e.g. IPB1B2 and the timecodes // I: 0, P: 120, B1: 40, B2: 80. if (!m_relaxed_timecode_checking && (pack->timecode < m_safety_last_timecode) && (0 > pack->fref) && hack_engaged(ENGAGE_ENABLE_TIMECODE_WARNING)) { if (track_audio == m_htrack_type) { int64_t needed_timecode_offset = m_safety_last_timecode + m_safety_last_duration - pack->timecode; m_correction_timecode_offset += needed_timecode_offset; pack->timecode += needed_timecode_offset; if (pack->has_bref()) pack->bref += needed_timecode_offset; if (pack->has_fref()) pack->fref += needed_timecode_offset; mxwarn_tid(m_ti.m_fname, m_ti.m_id, boost::format(Y("The current packet's timecode is smaller than that of the previous packet. " "This usually means that the source file is a Matroska file that has not been created 100%% correctly. " "The timecodes of all packets will be adjusted by %1%ms in order not to lose any data. " "This may throw audio/video synchronization off, but that can be corrected with mkvmerge's \"--sync\" option. " "If you already use \"--sync\" and you still get this warning then do NOT worry -- this is normal. " "If this error happens more than once and you get this message more than once for a particular track " "then either is the source file badly mastered, or mkvmerge contains a bug. " "In this case you should contact the author Moritz Bunkus <*****@*****.**>.\n")) % ((needed_timecode_offset + 500000) / 1000000)); } else mxwarn_tid(m_ti.m_fname, m_ti.m_id, boost::format("generic_packetizer_c::add_packet2: timecode < last_timecode (%1% < %2%). %3%\n") % format_timestamp(pack->timecode) % format_timestamp(m_safety_last_timecode) % BUGMSG); } m_safety_last_timecode = pack->timecode; m_safety_last_duration = pack->duration; pack->timecode_before_factory = pack->timecode; m_packet_queue.push_back(pack); if (!m_timestamp_factory || (TFA_IMMEDIATE == m_timestamp_factory_application_mode)) apply_factory_once(pack); else apply_factory(); }
void timestamp_calculator_c::add_timecode(packet_cptr const &packet) { if (packet->has_timecode()) add_timecode(timestamp_c::ns(packet->timecode)); }