EbmlElement * kax_file_c::read_next_level1_element(uint32_t wanted_id, bool report_cluster_timecode) { try { auto element = read_next_level1_element_internal(wanted_id); if (report_cluster_timecode && (-1 != m_timecode_scale)) mxinfo(boost::format(Y("The first cluster timecode after the resync is %1%.\n")) % format_timecode(FindChildValue<KaxClusterTimecode>(static_cast<KaxCluster *>(element)) * m_timecode_scale)); return element; } catch (mtx::mm_io::exception &e) { mxwarn(boost::format("%1% %2% %3%\n") % (boost::format(Y("%1%: an exception occurred (message: %2%; type: %3%).")) % "kax_file_c::read_next_level1_element()" % (boost::format("%1% / %2%") % e.what() % e.error()) % typeid(e).name()) % Y("This usually indicates a damaged file structure.") % Y("The file will not be processed further.")); } catch (std::exception &e) { mxwarn(boost::format("%1% %2% %3%\n") % (boost::format(Y("%1%: an exception occurred (message: %2%; type: %3%).")) % "kax_file_c::read_next_level1_element()" % e.what() % typeid(e).name()) % Y("This usually indicates a damaged file structure.") % Y("The file will not be processed further.")); } catch (...) { mxwarn(boost::format("%1% %2% %3%\n") % (boost::format(Y("%1%: an unknown exception occurred.")) % "kax_file_c::read_next_level1_element()") % Y("This usually indicates a damaged file structure.") % Y("The file will not be processed further.")); } return nullptr; }
void xtr_srt_c::handle_frame(xtr_frame_t &f) { if (-1 == f.duration) { mxwarn(boost::format(Y("Track %1%: Subtitle entry number %2% is missing its duration. Assuming a duration of 1s.\n")) % m_tid % (m_num_entries + 1)); f.duration = 1000000000; } int64_t start = f.timecode / 1000000; int64_t end = start + f.duration / 1000000; ++m_num_entries; char *text = new char[f.frame->get_size() + 1]; memcpy(text, f.frame->get_buffer(), f.frame->get_size()); text[f.frame->get_size()] = 0; std::string buffer = (boost::format("%1%\n" "%|2$02d|:%|3$02d|:%|4$02d|,%|5$03d| --> %|6$02d|:%|7$02d|:%|8$02d|,%|9$03d|\n" "%10%\n\n") % m_num_entries % (start / 1000 / 60 / 60) % ((start / 1000 / 60) % 60) % ((start / 1000) % 60) % (start % 1000) % (end / 1000 / 60 / 60) % ((end / 1000 / 60) % 60) % ((end / 1000) % 60) % (end % 1000) % m_conv->native(text) ).str(); m_out->puts(buffer); m_out->flush(); delete []text; }
bool xtr_avc_c::write_nal(const binary *data, size_t &pos, size_t data_size, size_t write_nal_size_size) { size_t i; size_t nal_size = 0; if (write_nal_size_size > data_size) return false; for (i = 0; i < write_nal_size_size; ++i) nal_size = (nal_size << 8) | data[pos++]; if ((pos + nal_size) > data_size) { mxwarn(boost::format(Y("Track %1%: NAL too big. Size according to header field: %2%, available bytes in packet: %3%. This NAL is defect and will be skipped.\n")) % m_tid % nal_size % (data_size - pos)); return false; } m_out->write(s_start_code, 4); m_out->write(data + pos, nal_size); pos += nal_size; return true; }
void options_c::merge_targets() { std::map<uint64_t, track_target_c *> targets_by_track_uid; std::vector<target_cptr> targets_to_keep; for (auto &target : m_targets) { auto track_target = dynamic_cast<track_target_c *>(target.get()); if (!track_target || dynamic_cast<segment_info_target_c *>(target.get())) { targets_to_keep.push_back(target); continue; } auto existing_target_it = targets_by_track_uid.find(track_target->get_track_uid()); auto track_uid = target->get_track_uid(); if (targets_by_track_uid.end() == existing_target_it) { targets_to_keep.push_back(target); targets_by_track_uid[track_uid] = track_target; continue; } existing_target_it->second->merge_changes(*track_target); mxwarn(boost::format(Y("The edit specifications '%1%' and '%2%' resolve to the same track with the UID %3%.\n")) % existing_target_it->second->get_spec() % track_target->get_spec() % track_uid); } m_targets.swap(targets_to_keep); }
bool xtr_hevc_c::write_nal(binary const *data, size_t &pos, size_t data_size, size_t write_nal_size_size) { if (write_nal_size_size > data_size) return false; auto nal_size = get_uint_be(&data[pos], write_nal_size_size); pos += write_nal_size_size; if ((pos + nal_size) > data_size) { mxwarn(boost::format(Y("Track %1%: NAL too big. Size according to header field: %2%, available bytes in packet: %3%. This NAL is defect and will be skipped.\n")) % m_tid % nal_size % (data_size - pos)); return false; } auto nal_unit_type = (data[pos] >> 1) & 0x3f; auto start_code_size = m_first_nalu || (HEVC_NALU_TYPE_VIDEO_PARAM == nal_unit_type) || (HEVC_NALU_TYPE_SEQ_PARAM == nal_unit_type) || (HEVC_NALU_TYPE_PIC_PARAM == nal_unit_type) ? 4 : 3; m_first_nalu = false; m_out->write(ms_start_code + (4 - start_code_size), start_code_size); m_out->write(data + pos, nal_size); pos += nal_size; return true; }
void ebml_chapters_converter_c::fix_edition_entry(KaxEditionEntry &eentry) const { bool atom_found = false; KaxEditionUID *euid = nullptr; for (auto element : eentry) if (dynamic_cast<KaxEditionUID *>(element)) { euid = static_cast<KaxEditionUID *>(element); if (!is_unique_number(uint64(*euid), UNIQUE_EDITION_IDS)) { mxwarn(boost::format(Y("Chapter parser: The EditionUID %1% is not unique and could not be reused. A new one will be created.\n")) % uint64(*euid)); *static_cast<EbmlUInteger *>(euid) = create_unique_number(UNIQUE_EDITION_IDS); } } else if (dynamic_cast<KaxChapterAtom *>(element)) { atom_found = true; fix_atom(static_cast<KaxChapterAtom &>(*element)); } if (!atom_found) throw conversion_x{Y("At least one <ChapterAtom> element is needed.")}; if (!euid) { euid = new KaxEditionUID; *static_cast<EbmlUInteger *>(euid) = create_unique_number(UNIQUE_EDITION_IDS); eentry.PushElement(*euid); } }
M2VParser::~M2VParser(){ DumpQueues(); if (!probing && !waitQueue.empty()) { mxwarn(Y("Video ended with a shortened group of pictures. Some frames have been dropped. You may want to fix the MPEG2 video stream before attempting to multiplex it.\n")); } FlushWaitQueue(); delete seqHdrChunk; delete gopChunk; delete mpgBuf; }
void generic_packetizer_c::show_experimental_status_version(std::string const &codec_id) { auto idx = get_format_name().get_untranslated(); if (s_experimental_status_warning_shown[idx]) return; s_experimental_status_warning_shown[idx] = true; mxwarn(boost::format(Y("Note that the Matroska specifications regarding the storage of '%1%' have not been finalized yet. " "mkvmerge's support for it is therefore subject to change and uses the CodecID '%2%/EXPERIMENTAL' instead of '%2%'. " "This warning will be removed once the specifications have been finalized and mkvmerge has been updated accordingly.\n")) % get_format_name().get_translated() % codec_id); }
void generic_packetizer_c::set_as_default_track(int type, int priority) { if (g_default_tracks_priority[type] < priority) { g_default_tracks_priority[type] = priority; g_default_tracks[type] = m_hserialno; } else if ( (DEFAULT_TRACK_PRIORITY_CMDLINE == priority) && (g_default_tracks[type] != m_hserialno) && !m_default_track_warning_printed) { mxwarn(boost::format(Y("Another default track for %1% tracks has already been set. The 'default' flag for track %2% of '%3%' will not be set.\n")) % (DEFTRACK_TYPE_AUDIO == type ? "audio" : DEFTRACK_TYPE_VIDEO == type ? "video" : "subtitle") % m_ti.m_id % m_ti.m_fname); m_default_track_warning_printed = true; } }
iconv_charset_converter_c::iconv_charset_converter_c(const std::string &charset) : charset_converter_c(charset) , m_is_utf8(false) , m_to_utf8_handle(reinterpret_cast<iconv_t>(-1)) , m_from_utf8_handle(reinterpret_cast<iconv_t>(-1)) { if (is_utf8_charset_name(charset)) { m_is_utf8 = true; return; } m_to_utf8_handle = iconv_open("UTF-8", charset.c_str()); if (reinterpret_cast<iconv_t>(-1) == m_to_utf8_handle) mxwarn(boost::format(Y("Could not initialize the iconv library for the conversion from %1% to UFT-8. " "Some strings will not be converted to UTF-8 and the resulting Matroska file " "might not comply with the Matroska specs (error: %2%, %3%).\n")) % charset % errno % strerror(errno)); m_from_utf8_handle = iconv_open(charset.c_str(), "UTF-8"); if (reinterpret_cast<iconv_t>(-1) == m_from_utf8_handle) mxwarn(boost::format(Y("Could not initialize the iconv library for the conversion from UFT-8 to %1%. " "Some strings cannot be converted from UTF-8 and might be displayed incorrectly (error: %2%, %3%).\n")) % charset % errno % strerror(errno)); }
void aac_reader_c::create_packetizer(int64_t) { if (!demuxing_requested('a', 0) || (NPTZR() != 0)) return; if (!m_sbr_status_set) mxwarn(Y("AAC files may contain HE-AAC / AAC+ / SBR AAC audio. " "This can NOT be detected automatically. Therefore you have to " "specifiy '--aac-is-sbr 0' manually for this input file if the " "file actually contains SBR AAC. The file will be muxed in the " "WRONG way otherwise. Also read mkvmerge's documentation.\n")); generic_packetizer_c *aacpacketizer = new aac_packetizer_c(this, m_ti, m_aacheader.id, m_aacheader.profile, m_aacheader.sample_rate, m_aacheader.channels, m_emphasis_present); add_packetizer(aacpacketizer); if (AAC_PROFILE_SBR == m_aacheader.profile) aacpacketizer->set_audio_output_sampling_freq(m_aacheader.sample_rate * 2); show_packetizer_info(0, aacpacketizer); }
void xtr_usf_c::finish_track() { auto subtitles = m_doc->document_element().append_child("subtitles"); subtitles.append_child("language").append_attribute("code").set_value(m_language.c_str()); for (auto &entry : m_entries) { std::string text = std::string{"<subtitle>"} + entry.m_text + "</subtitle>"; strip(text, true); std::stringstream text_in(text); pugi::xml_document subtitle_doc; if (!subtitle_doc.load(text_in, pugi::parse_default | pugi::parse_declaration | pugi::parse_doctype | pugi::parse_pi | pugi::parse_comments)) { mxwarn(boost::format(Y("Track %1%: An USF subtitle entry starting at timecode %2% is not well-formed XML and will be skipped.\n")) % m_tid % format_timecode(entry.m_start * 1000000, 3)); continue; } auto subtitle = subtitles.append_child("subtitle"); subtitle.append_attribute("start").set_value(format_timecode(entry.m_start * 1000000, 3).c_str()); subtitle.append_attribute("stop"). set_value(format_timecode(entry.m_end * 1000000, 3).c_str()); for (auto child : subtitle_doc.document_element()) subtitle.append_copy(child); } }
void timecode_factory_v3_c::parse(mm_io_c &in) { std::string line; timecode_duration_c t; std::vector<timecode_duration_c>::iterator iit; std::vector<timecode_duration_c>::const_iterator pit; std::string err_msg_assume = (boost::format(Y("The timecode file '%1%' does not contain a valid 'Assume' line with the default number of frames per second.\n")) % m_file_name).str(); int line_no = 1; do { if (!in.getline2(line)) mxerror(err_msg_assume); line_no++; strip(line); if ((line.length() != 0) && (line[0] != '#')) break; } while (true); if (!ba::istarts_with(line, "assume ")) mxerror(err_msg_assume); line.erase(0, 6); strip(line); if (!parse_double(line.c_str(), m_default_fps)) mxerror(err_msg_assume); while (in.getline2(line)) { line_no++; strip(line, true); if ((line.length() == 0) || (line[0] == '#')) continue; double dur; if (ba::istarts_with(line, "gap,")) { line.erase(0, 4); strip(line); t.is_gap = true; t.fps = m_default_fps; if (!parse_double(line.c_str(), dur)) mxerror(boost::format(Y("The timecode file '%1%' does not contain a valid 'Gap' line with the duration of the gap.\n")) % m_file_name); t.duration = (int64_t)(1000000000.0 * dur); } else { t.is_gap = false; std::vector<std::string> parts = split(line, ","); if ((1 == parts.size()) && parse_double(parts[0], dur)) t.fps = m_default_fps; else if ((2 != parts.size()) || !parse_double(parts[1], t.fps)) { mxwarn(boost::format(Y("Line %1% of the timecode file '%2%' could not be parsed.\n")) % line_no % m_file_name); continue; } t.duration = (int64_t)(1000000000.0 * dur); } if ((t.fps < 0) || (t.duration <= 0)) { mxwarn(boost::format(Y("Line %1% of the timecode file '%2%' contains inconsistent data (e.g. the duration or the FPS are smaller than zero).\n")) % line_no % m_file_name); continue; } m_durations.push_back(t); } mxverb(3, boost::format("ext_timecodes: Version 3, default fps %1%, %2% entries.\n") % m_default_fps % m_durations.size()); if (m_durations.size() == 0) mxwarn(boost::format(Y("The timecode file '%1%' does not contain any valid entry.\n")) % m_file_name); t.duration = 0xfffffffffffffffll; t.is_gap = false; t.fps = m_default_fps; m_durations.push_back(t); for (iit = m_durations.begin(); iit < m_durations.end(); iit++) mxverb(4, boost::format("durations:%1% entry for %2% with %3% FPS\n") % (iit->is_gap ? " gap" : "") % iit->duration % iit->fps); }
void avi_reader_c::add_audio_demuxer(int aid) { for (auto &demuxer : m_audio_demuxers) if (demuxer.m_aid == aid) // Demuxer already added? return; AVI_set_audio_track(m_avi, aid); if (AVI_read_audio_chunk(m_avi, nullptr) < 0) { mxwarn(boost::format(Y("Could not find an index for audio track %1% (avilib error message: %2%). Skipping track.\n")) % (aid + 1) % AVI_strerror()); return; } avi_demuxer_t demuxer; generic_packetizer_c *packetizer = nullptr; alWAVEFORMATEX *wfe = m_avi->wave_format_ex[aid]; uint32_t audio_format = AVI_audio_format(m_avi); demuxer.m_aid = aid; demuxer.m_ptzr = -1; demuxer.m_samples_per_second = AVI_audio_rate(m_avi); demuxer.m_channels = AVI_audio_channels(m_avi); demuxer.m_bits_per_sample = AVI_audio_bits(m_avi); m_ti.m_id = aid + 1; // ID for this audio track. auto stream_header = &m_avi->stream_headers[aid]; auto dw_scale = static_cast<int64_t>(get_uint32_le(&stream_header->dw_scale)); auto dw_rate = static_cast<int64_t>(get_uint32_le(&stream_header->dw_rate)); auto dw_sample_size = static_cast<int64_t>(get_uint32_le(&stream_header->dw_sample_size)); m_ti.m_avi_audio_data_rate = dw_scale ? dw_rate * dw_sample_size / dw_scale : 0; if ((0xfffe == audio_format) && (get_uint16_le(&wfe->cb_size) >= (sizeof(alWAVEFORMATEXTENSION)))) { alWAVEFORMATEXTENSIBLE *ext = reinterpret_cast<alWAVEFORMATEXTENSIBLE *>(wfe); audio_format = get_uint32_le(&ext->extension.guid.data1); } else if (get_uint16_le(&wfe->cb_size) > 0) m_ti.m_private_data = memory_c::clone(wfe + 1, get_uint16_le(&wfe->cb_size)); else m_ti.m_private_data.reset(); demuxer.m_codec = codec_c::look_up_audio_format(audio_format); if (demuxer.m_codec.is(codec_c::type_e::A_PCM)) packetizer = new pcm_packetizer_c(this, m_ti, demuxer.m_samples_per_second, demuxer.m_channels, demuxer.m_bits_per_sample, 0x0003 == audio_format ? pcm_packetizer_c::ieee_float : pcm_packetizer_c::little_endian_integer); else if (demuxer.m_codec.is(codec_c::type_e::A_MP2) || demuxer.m_codec.is(codec_c::type_e::A_MP3)) packetizer = new mp3_packetizer_c(this, m_ti, demuxer.m_samples_per_second, demuxer.m_channels, false); else if (demuxer.m_codec.is(codec_c::type_e::A_AC3)) packetizer = new ac3_packetizer_c(this, m_ti, demuxer.m_samples_per_second, demuxer.m_channels, 0); else if (demuxer.m_codec.is(codec_c::type_e::A_DTS)) packetizer = create_dts_packetizer(aid); else if (demuxer.m_codec.is(codec_c::type_e::A_AAC)) packetizer = create_aac_packetizer(aid, demuxer); else if (demuxer.m_codec.is(codec_c::type_e::A_VORBIS)) packetizer = create_vorbis_packetizer(aid); else mxerror_tid(m_ti.m_fname, aid + 1, boost::format(Y("Unknown/unsupported audio format 0x%|1$04x| for this audio track.\n")) % audio_format); packetizer->enable_avi_audio_sync(true); show_packetizer_info(aid + 1, packetizer); demuxer.m_ptzr = add_packetizer(packetizer); m_audio_demuxers.push_back(demuxer); int i, maxchunks = AVI_audio_chunks(m_avi); for (i = 0; i < maxchunks; i++) { auto size = AVI_audio_size(m_avi, i); if (size < AVI_MAX_AUDIO_CHUNK_SIZE) m_bytes_to_process += size; } }
void avi_reader_c::add_audio_demuxer(int aid) { for (auto &demuxer : m_audio_demuxers) if (demuxer.m_aid == aid) // Demuxer already added? return; AVI_set_audio_track(m_avi, aid); if (AVI_read_audio_chunk(m_avi, nullptr) < 0) { mxwarn(boost::format(Y("Could not find an index for audio track %1% (avilib error message: %2%). Skipping track.\n")) % (aid + 1) % AVI_strerror()); return; } avi_demuxer_t demuxer; generic_packetizer_c *packetizer = nullptr; alWAVEFORMATEX *wfe = m_avi->wave_format_ex[aid]; uint32_t audio_format = AVI_audio_format(m_avi); demuxer.m_aid = aid; demuxer.m_ptzr = -1; demuxer.m_samples_per_second = AVI_audio_rate(m_avi); demuxer.m_channels = AVI_audio_channels(m_avi); demuxer.m_bits_per_sample = AVI_audio_bits(m_avi); m_ti.m_id = aid + 1; // ID for this audio track. m_ti.m_avi_block_align = get_uint16_le(&wfe->n_block_align); m_ti.m_avi_avg_bytes_per_sec = get_uint32_le(&wfe->n_avg_bytes_per_sec); m_ti.m_avi_samples_per_chunk = get_uint32_le(&m_avi->stream_headers[aid].dw_scale); m_ti.m_avi_sample_scale = get_uint32_le(&m_avi->stream_headers[aid].dw_rate); m_ti.m_avi_samples_per_sec = demuxer.m_samples_per_second; if ((0xfffe == audio_format) && (get_uint16_le(&wfe->cb_size) >= (sizeof(alWAVEFORMATEXTENSION)))) { alWAVEFORMATEXTENSIBLE *ext = reinterpret_cast<alWAVEFORMATEXTENSIBLE *>(wfe); audio_format = get_uint32_le(&ext->extension.guid.data1); } else if (get_uint16_le(&wfe->cb_size) > 0) m_ti.m_private_data = memory_c::clone(wfe + 1, get_uint16_le(&wfe->cb_size)); else m_ti.m_private_data.reset(); switch(audio_format) { case 0x0001: // raw PCM audio case 0x0003: // raw PCM audio (float) packetizer = new pcm_packetizer_c(this, m_ti, demuxer.m_samples_per_second, demuxer.m_channels, demuxer.m_bits_per_sample, 0x0003 == audio_format ? pcm_packetizer_c::ieee_float : pcm_packetizer_c::little_endian_integer); break; case 0x0050: // MP2 case 0x0055: // MP3 packetizer = new mp3_packetizer_c(this, m_ti, demuxer.m_samples_per_second, demuxer.m_channels, false); break; case 0x2000: // AC3 packetizer = new ac3_packetizer_c(this, m_ti, demuxer.m_samples_per_second, demuxer.m_channels, 0); break; case 0x2001: // DTS packetizer = create_dts_packetizer(aid); break; case 0x00ff: case 0x706d: // AAC packetizer = create_aac_packetizer(aid, demuxer); break; case 0x566f: // Vorbis packetizer = create_vorbis_packetizer(aid); break; default: mxerror_tid(m_ti.m_fname, aid + 1, boost::format(Y("Unknown/unsupported audio format 0x%|1$04x| for this audio track.\n")) % audio_format); } show_packetizer_info(aid + 1, packetizer); demuxer.m_ptzr = add_packetizer(packetizer); m_audio_demuxers.push_back(demuxer); int i, maxchunks = AVI_audio_chunks(m_avi); for (i = 0; i < maxchunks; i++) m_bytes_to_process += AVI_audio_size(m_avi, i); }
int64_t spu_extract_duration(unsigned char *data, size_t buf_size, int64_t timecode) { uint32_t date, control_start, next_off, start_off, off; unsigned char type; int duration; bool unknown; control_start = get_uint16_be(data + 2); next_off = control_start; duration = -1; start_off = 0; while ((start_off != next_off) && (next_off < buf_size)) { start_off = next_off; date = get_uint16_be(data + start_off) * 1024; next_off = get_uint16_be(data + start_off + 2); if (next_off < start_off) { mxwarn(boost::format(Y("spu_extraction_duration: Encountered broken SPU packet (next_off < start_off) at timecode %1%. " "This packet might be displayed incorrectly or not at all.\n")) % format_timecode(timecode, 3)); return -1; } mxverb(4, boost::format("spu_extraction_duration: date = %1%\n") % date); off = start_off + 4; for (type = data[off++]; type != 0xff; type = data[off++]) { mxverb(4, boost::format("spu_extraction_duration: cmd = %1% ") % type); unknown = false; switch(type) { case 0x00: /* Menu ID, 1 byte */ mxverb(4, "menu ID"); break; case 0x01: /* Start display */ mxverb(4, "start display"); break; case 0x02: /* Stop display */ mxverb(4, boost::format("stop display: %1%") % (date / 90)); return (int64_t)date * 1000000 / 90; break; case 0x03: /* Palette */ mxverb(4, "palette"); off+=2; break; case 0x04: /* Alpha */ mxverb(4, "alpha"); off+=2; break; case 0x05: mxverb(4, "coords"); off+=6; break; case 0x06: mxverb(4, "graphic lines"); off+=4; break; case 0xff: /* All done, bye-bye */ mxverb(4, "done"); return duration; default: mxverb(4, boost::format("unknown (0x%|1$02x|), skipping %2% bytes.") % type % (next_off - off)); unknown = true; } mxverb(4, "\n"); if (unknown) break; } } return duration; }
//Maintains the time of the last start of GOP and uses the temporal_reference //field as an offset. int32_t M2VParser::FillQueues(){ if(chunks.empty()){ return -1; } bool done = false; while(!done){ MediaTime myTime; MPEGChunk* chunk = chunks.front(); while (chunk->GetType() != MPEG_VIDEO_PICTURE_START_CODE) { if (chunk->GetType() == MPEG_VIDEO_GOP_START_CODE) { ParseGOPHeader(chunk, m_gopHdr); if (frameNum != 0) { gopPts = highestPts + 1; } if (gopChunk) delete gopChunk; gopChunk = chunk; gopNum++; /* Perform some sanity checks */ if(waitSecondField){ mxerror(Y("Single field frame before GOP header detected. Fix the MPEG2 video stream before attempting to multiplex it.\n")); } if(!waitQueue.empty()){ mxwarn(Y("Shortened GOP detected. Some frames have been dropped. You may want to fix the MPEG2 video stream before attempting to multiplex it.\n")); FlushWaitQueue(); } if(m_gopHdr.brokenLink){ mxinfo(Y("Found group of picture with broken link. You may want use smart reencode before attempting to multiplex it.\n")); } // There are too many broken videos to do the following so ReferenceBlock will be wrong for broken videos. /* if(m_gopHdr.closedGOP){ ClearRef(); } */ } else if (chunk->GetType() == MPEG_VIDEO_SEQUENCE_START_CODE) { if (seqHdrChunk) delete seqHdrChunk; ParseSequenceHeader(chunk, m_seqHdr); seqHdrChunk = chunk; } chunks.erase(chunks.begin()); if (chunks.empty()) return -1; chunk = chunks.front(); } MPEG2PictureHeader picHdr; ParsePictureHeader(chunk, picHdr); if (picHdr.pictureStructure == 0x03) { usePictureFrames = true; } myTime = gopPts + picHdr.temporalReference; invisible = false; if (myTime > highestPts) highestPts = myTime; switch(picHdr.frameType){ case MPEG2_I_FRAME: PrepareFrame(chunk, myTime, picHdr); notReachedFirstGOP = false; break; case MPEG2_P_FRAME: if(firstRef == -1) break; PrepareFrame(chunk, myTime, picHdr); break; default: //B-frames if(firstRef == -1 || secondRef == -1){ if(!m_gopHdr.closedGOP && !m_gopHdr.brokenLink){ if(gopNum > 0){ mxerror(Y("Found B frame without second reference in a non closed GOP. Fix the MPEG2 video stream before attempting to multiplex it.\n")); } else if (!probing && !bFrameMissingReferenceWarning){ mxwarn(Y("Found one or more B frames without second reference in the first GOP. You may want to fix the MPEG2 video stream or use smart reencode before attempting to multiplex it.\n")); bFrameMissingReferenceWarning = true; } } invisible = true; } PrepareFrame(chunk, myTime, picHdr); } frameNum++; chunks.erase(chunks.begin()); delete chunk; if (chunks.empty()) return -1; } return 0; }
bool content_decoder_c::initialize(KaxTrackEntry &ktentry) { encodings.clear(); KaxContentEncodings *kcencodings = FindChild<KaxContentEncodings>(&ktentry); if (!kcencodings) return true; int tid = kt_get_number(ktentry); for (auto kcenc_el : *kcencodings) { auto kcenc = dynamic_cast<KaxContentEncoding *>(kcenc_el); if (!kcenc) continue; kax_content_encoding_t enc; enc.order = FindChildValue<KaxContentEncodingOrder>(kcenc); enc.type = FindChildValue<KaxContentEncodingType >(kcenc); enc.scope = FindChildValue<KaxContentEncodingScope>(kcenc, 1u); auto ce_comp = FindChild<KaxContentCompression>(kcenc); if (ce_comp) { enc.comp_algo = FindChildValue<KaxContentCompAlgo >(ce_comp); enc.comp_settings = FindChildValue<KaxContentCompSettings>(ce_comp); } auto ce_enc = FindChild<KaxContentEncryption>(kcenc); if (ce_enc) { enc.enc_algo = FindChildValue<KaxContentEncAlgo >(ce_enc); enc.enc_keyid = FindChildValue<KaxContentEncKeyID >(ce_enc); enc.sig_algo = FindChildValue<KaxContentSigAlgo >(ce_enc); enc.sig_hash_algo = FindChildValue<KaxContentSigHashAlgo>(ce_enc); enc.sig_keyid = FindChildValue<KaxContentSigKeyID >(ce_enc); enc.signature = FindChildValue<KaxContentSignature >(ce_enc); } if (1 == enc.type) { mxwarn(boost::format(Y("Track number %1% has been encrypted and decryption has not yet been implemented.\n")) % tid); ok = false; break; } if (0 != enc.type) { mxerror(boost::format(Y("Unknown content encoding type %1% for track %2%.\n")) % enc.type % tid); ok = false; break; } if (0 == enc.comp_algo) enc.compressor = std::shared_ptr<compressor_c>(new zlib_compressor_c()); else if (mtx::included_in(enc.comp_algo, 1u, 2u)) { auto algorithm = 1u == enc.comp_algo ? "bzlib" : "lzo1x"; mxwarn(boost::format(Y("Track %1% was compressed with the algorithm '%2%' which is not supported anymore.\n")) % tid % algorithm); ok = false; break; } else if (3 == enc.comp_algo) { enc.compressor = std::shared_ptr<compressor_c>(new header_removal_compressor_c); std::static_pointer_cast<header_removal_compressor_c>(enc.compressor)->set_bytes(enc.comp_settings); } else { mxwarn(boost::format(Y("Track %1% has been compressed with an unknown/unsupported compression algorithm (%2%).\n")) % tid % enc.comp_algo); ok = false; break; } encodings.push_back(enc); } brng::stable_sort(encodings, [](kax_content_encoding_t const &a, kax_content_encoding_t const &b) { return a.order < b.order; }); return ok; }
void timecode_factory_v1_c::parse(mm_io_c &in) { std::string line; timecode_range_c t; std::vector<timecode_range_c>::iterator iit; std::vector<timecode_range_c>::const_iterator pit; int line_no = 1; do { if (!in.getline2(line)) mxerror(boost::format(Y("The timecode file '%1%' does not contain a valid 'Assume' line with the default number of frames per second.\n")) % m_file_name); line_no++; strip(line); if (!line.empty() && ('#' != line[0])) break; } while (true); if (!ba::istarts_with(line, "assume ")) mxerror(boost::format(Y("The timecode file '%1%' does not contain a valid 'Assume' line with the default number of frames per second.\n")) % m_file_name); line.erase(0, 6); strip(line); if (!parse_double(line.c_str(), m_default_fps)) mxerror(boost::format(Y("The timecode file '%1%' does not contain a valid 'Assume' line with the default number of frames per second.\n")) % m_file_name); while (in.getline2(line)) { line_no++; strip(line, true); if (line.empty() || ('#' == line[0])) continue; std::vector<std::string> parts = split(line, ",", 3); if ( (parts.size() != 3) || !parse_uint(parts[0], t.start_frame) || !parse_uint(parts[1], t.end_frame) || !parse_double(parts[2], t.fps)) { mxwarn(boost::format(Y("Line %1% of the timecode file '%2%' could not be parsed.\n")) % line_no % m_file_name); continue; } if ((t.fps <= 0) || (t.end_frame < t.start_frame)) { mxwarn(boost::format(Y("Line %1% of the timecode file '%2%' contains inconsistent data (e.g. the start frame number is bigger than the end frame " "number, or some values are smaller than zero).\n")) % line_no % m_file_name); continue; } m_ranges.push_back(t); } mxverb(3, boost::format("ext_timecodes: Version 1, default fps %1%, %2% entries.\n") % m_default_fps % m_ranges.size()); if (m_ranges.size() == 0) t.start_frame = 0; else { std::sort(m_ranges.begin(), m_ranges.end()); bool done; do { done = true; iit = m_ranges.begin(); size_t i; for (i = 0; i < (m_ranges.size() - 1); i++) { iit++; if (m_ranges[i].end_frame < (m_ranges[i + 1].start_frame - 1)) { t.start_frame = m_ranges[i].end_frame + 1; t.end_frame = m_ranges[i + 1].start_frame - 1; t.fps = m_default_fps; m_ranges.insert(iit, t); done = false; break; } } } while (!done); if (m_ranges[0].start_frame != 0) { t.start_frame = 0; t.end_frame = m_ranges[0].start_frame - 1; t.fps = m_default_fps; m_ranges.insert(m_ranges.begin(), t); } t.start_frame = m_ranges[m_ranges.size() - 1].end_frame + 1; } t.end_frame = 0xfffffffffffffffll; t.fps = m_default_fps; m_ranges.push_back(t); m_ranges[0].base_timecode = 0.0; pit = m_ranges.begin(); for (iit = m_ranges.begin() + 1; iit < m_ranges.end(); iit++, pit++) iit->base_timecode = pit->base_timecode + ((double)pit->end_frame - (double)pit->start_frame + 1) * 1000000000.0 / pit->fps; for (iit = m_ranges.begin(); iit < m_ranges.end(); iit++) mxverb(3, boost::format("ranges: entry %1% -> %2% at %3% with %4%\n") % iit->start_frame % iit->end_frame % iit->fps % iit->base_timecode); }
void mxwarn_fn(const std::string &file_name, const std::string &warning) { mxwarn(boost::format(Y("'%1%': %2%")) % file_name % warning); }
void kax_file_c::report(std::string const &message) { if (m_reporting_enabled) mxwarn(message); }
void kax_file_c::report(boost::format const &message) { if (m_reporting_enabled) mxwarn(message); }
void mxwarn_tid(const std::string &file_name, int64_t track_id, const std::string &warning) { mxwarn(boost::format(Y("'%1%' track %2%: %3%")) % file_name % track_id % warning); }
void xtr_ssa_c::handle_frame(xtr_frame_t &f) { if (0 > f.duration) { mxwarn(boost::format(Y("Subtitle track %1% is missing some duration elements. " "Please check the resulting SSA/ASS file for entries that have the same start and end time.\n")) % m_tid); m_warning_printed = true; } int64_t start = f.timecode / 1000000; int64_t end = start + f.duration / 1000000; char *s = (char *)safemalloc(f.frame->get_size() + 1); memory_c af_s((unsigned char *)s, 0, true); memcpy(s, f.frame->get_buffer(), f.frame->get_size()); s[f.frame->get_size()] = 0; // Split the line into the fields. // Specs say that the following fields are to put into the block: // 0: ReadOrder, 1: Layer, 2: Style, 3: Name, 4: MarginL, 5: MarginR, // 6: MarginV, 7: Effect, 8: Text std::vector<std::string> fields = split(s, ",", 9); if (9 < fields.size()) { mxwarn(boost::format(Y("Invalid format for a SSA line ('%1%') at timecode %2%: Too many fields found (%3% instead of 9). This entry will be skipped.\n")) % s % format_timecode(f.timecode * 1000000, 3) % fields.size()); return; } while (9 != fields.size()) fields.push_back(""); // Convert the ReadOrder entry so that we can re-order the entries later. int num; if (!parse_number(fields[0], num)) { mxwarn(boost::format(Y("Invalid format for a SSA line ('%1%') at timecode %2%: The first field is not an integer. This entry will be skipped.\n")) % s % format_timecode(f.timecode * 1000000, 3)); return; } // Reconstruct the 'original' line. It'll look like this for SSA: // Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text // and for ASS: // Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text // Problem is that the CodecPrivate may contain a Format: line // that defines a different layout. So let's account for that. std::string line = "Dialogue: "; size_t i; for (i = 0; i < m_ssa_format.size(); i++) { std::string format = m_ssa_format[i]; if (balg::iequals(format, "actor")) format = "name"; if (0 < i) line += ","; if (format == "marked") line += "Marked=0"; else if (format == "start") line += (boost::format("%1%:%|2$02d|:%|3$02d|.%|4$02d|") % (start / 1000 / 60 / 60) % ((start / 1000 / 60) % 60) % ((start / 1000) % 60) % ((start % 1000) / 10)).str(); else if (format == "end") line += (boost::format("%1%:%|2$02d|:%|3$02d|.%|4$02d|") % (end / 1000 / 60 / 60) % ((end / 1000 / 60) % 60) % ((end / 1000) % 60) % ((end % 1000) / 10)).str(); else { int k; for (k = 0; ms_kax_ssa_fields[k]; ++k) if (format == ms_kax_ssa_fields[k]) { line += fields[k]; break; } } } // Do the charset conversion. line = m_conv->native(line); line += "\n"; // Now store that entry. m_lines.push_back(ssa_line_c(line, num)); }