bool truehd_reader_c::find_valid_headers(mm_io_c &in, int64_t probe_range, int num_headers) { try { memory_cptr buf(memory_c::alloc(probe_range)); in.setFilePointer(0, seek_beginning); skip_id3v2_tag(in); int num_read = in.read(buf->get_buffer(), probe_range); truehd_parser_c parser; parser.add_data(buf->get_buffer(), num_read); int num_sync_frames = 0; while (parser.frame_available()) { truehd_frame_cptr frame = parser.get_next_frame(); if (frame->is_sync()) ++num_sync_frames; } return num_sync_frames >= num_headers; } catch (...) { return false; } }
void timecode_factory_v2_c::parse(mm_io_c &in) { std::string line; std::map<int64_t, int64_t> dur_map; int64_t dur_sum = 0; int line_no = 0; double previous_timecode = 0; while (in.getline2(line)) { line_no++; strip(line); if ((line.length() == 0) || (line[0] == '#')) continue; double timecode; if (!parse_double(line.c_str(), timecode)) mxerror(boost::format(Y("The line %1% of the timecode file '%2%' does not contain a valid floating point number.\n")) % line_no % m_file_name); if ((2 == m_version) && (timecode < previous_timecode)) mxerror(boost::format(Y("The timecode v2 file '%1%' contains timecodes that are not ordered. " "Due to a bug in mkvmerge versions up to and including v1.5.0 this was necessary " "if the track to which the timecode file was applied contained B frames. " "Starting with v1.5.1 mkvmerge now handles this correctly, and the timecodes in the timecode file must be ordered normally. " "For example, the frame sequence 'IPBBP...' at 25 FPS requires a timecode file with " "the first timecodes being '0', '40', '80', '120' etc and not '0', '120', '40', '80' etc.\n\n" "If you really have to specify non-sorted timecodes then use the timecode format v4. " "It is identical to format v2 but allows non-sorted timecodes.\n")) % in.get_file_name()); previous_timecode = timecode; m_timecodes.push_back((int64_t)(timecode * 1000000)); if (m_timecodes.size() > 1) { int64_t duration = m_timecodes[m_timecodes.size() - 1] - m_timecodes[m_timecodes.size() - 2]; if (dur_map.find(duration) == dur_map.end()) dur_map[duration] = 1; else dur_map[duration] = dur_map[duration] + 1; dur_sum += duration; m_durations.push_back(duration); } } if (m_timecodes.empty()) mxerror(boost::format(Y("The timecode file '%1%' does not contain any valid entry.\n")) % m_file_name); dur_sum = -1; std::pair<int64_t, int64_t> entry; for (auto entry : dur_map) { if ((0 > dur_sum) || (dur_map[dur_sum] < entry.second)) dur_sum = entry.first; mxverb(4, boost::format("ext_m_timecodes v2 dur_map %1% = %2%\n") % entry.first % entry.second); } mxverb(4, boost::format("ext_m_timecodes v2 max is %1% = %2%\n") % dur_sum % dur_map[dur_sum]); if (0 < dur_sum) m_default_fps = (double)1000000000.0 / dur_sum; m_durations.push_back(dur_sum); }
void cues_c::write(mm_io_c &out, KaxSeekHead &seek_head) { if (!m_points.size() || !g_cue_writing_requested) return; // auto start = get_current_time_millis(); sort(); // auto end_sort = get_current_time_millis(); // Need to write the (empty) cues element so that its position will // be set for indexing in g_kax_sh_main. Necessary because there's // no API function to force the position to a certain value; nor is // there a different API function in KaxSeekHead for adding anything // by ID and position manually. out.save_pos(); kax_cues_position_dummy_c cues_dummy; cues_dummy.Render(out); out.restore_pos(); // Write meta seek information if it is not disabled. seek_head.IndexThis(cues_dummy, *g_kax_segment); // Forcefully write the correct head and copy its content from the // temporary storage location. auto total_size = calculate_total_size(); write_ebml_element_head(out, EBML_ID(KaxCues), total_size); for (auto &point : m_points) { KaxCuePoint kc_point; GetChild<KaxCueTime>(kc_point).SetValue(point.timecode / g_timecode_scale); auto &positions = GetChild<KaxCueTrackPositions>(kc_point); GetChild<KaxCueTrack>(positions).SetValue(point.track_num); GetChild<KaxCueClusterPosition>(positions).SetValue(point.cluster_position); auto codec_state_position = m_codec_state_position_map.find({ point.track_num, point.timecode }); if (codec_state_position != m_codec_state_position_map.end()) GetChild<KaxCueCodecState>(positions).SetValue(codec_state_position->second); if (point.relative_position) GetChild<KaxCueRelativePosition>(positions).SetValue(point.relative_position); if (point.duration) GetChild<KaxCueDuration>(positions).SetValue(RND_TIMECODE_SCALE(point.duration) / g_timecode_scale); kc_point.Render(out); } m_points.clear(); m_codec_state_position_map.clear(); m_num_cue_points_postprocessed = 0; // auto end_all = get_current_time_millis(); // mxinfo(boost::format("dur sort %1% write %2% total %3%\n") % (end_sort - start) % (end_all - end_sort) % (end_all - start)); }
int aac_reader_c::find_valid_headers(mm_io_c &in, int64_t probe_range, int num_headers) { try { in.setFilePointer(0, seek_beginning); memory_cptr buf = memory_c::alloc(probe_range); int num_read = in.read(buf->get_buffer(), probe_range); in.setFilePointer(0, seek_beginning); return find_consecutive_aac_headers(buf->get_buffer(), num_read, num_headers); } catch (...) { return -1; } }
bool mpeg_es_reader_c::read_frame(M2VParser &parser, mm_io_c &in, int64_t max_size) { auto af_buffer = memory_c::alloc(READ_SIZE); auto buffer = af_buffer->get_buffer(); int bytes_probed = 0; while (true) { auto state = parser.GetState(); if (MPV_PARSER_STATE_FRAME == state) return true; if ((MPV_PARSER_STATE_EOS == state) || (MPV_PARSER_STATE_ERROR == state)) return false; assert(MPV_PARSER_STATE_NEED_DATA == state); if ((max_size != -1) && (bytes_probed > max_size)) return false; int bytes_read = in.read(buffer, std::min<int>(parser.GetFreeBufferSpace(), READ_SIZE)); if (!bytes_read) return false; bytes_probed += bytes_read; parser.WriteData(buffer, bytes_read); parser.SetEOS(); } }
static void _print_if_global(mm_io_c &out, const char *name, const char *format, int64_t tuid, KaxTags &tags) { std::string global = get_global_tag(name, tuid, tags); if (!global.empty()) out.puts(boost::format(format) % global); }
static void _print_if_available(mm_io_c &out, const char *name, const char *format, int64_t tuid, KaxTags &tags, KaxTag &tag) { std::string value = get_simple_tag_value(name, tag); if (!value.empty() && (value != get_global_tag(name, tuid, tags))) out.puts(boost::format(format) % value); }
static void print_comments(const char *prefix, KaxTag &tag, mm_io_c &out) { size_t i; for (i = 0; i < tag.ListSize(); i++) if (is_id(tag[i], KaxTagSimple) && ( (get_simple_tag_name(*static_cast<KaxTagSimple *>(tag[i])) == "COMMENT") || (get_simple_tag_name(*static_cast<KaxTagSimple *>(tag[i])) == "COMMENTS"))) out.puts(boost::format("%1%REM \"%2%\"\n") % prefix % get_simple_tag_value(*static_cast<KaxTagSimple *>(tag[i]))); }
int write_ebml_element_head(mm_io_c &out, EbmlId const &id, int64_t content_size) { int id_size = EBML_ID_LENGTH(id); int coded_size = CodedSizeLength(content_size, 0); uint8_t buffer[4 + 8]; id.Fill(buffer); CodedValueLength(content_size, coded_size, &buffer[id_size]); return out.write(buffer, id_size + coded_size); }
int ac3_reader_c::find_valid_headers(mm_io_c &in, int64_t probe_range, int num_headers) { try { memory_cptr buf(memory_c::alloc(probe_range)); in.setFilePointer(0, seek_beginning); skip_id3v2_tag(in); ac3::parser_c parser; int num_read = in.read(buf->get_buffer(), probe_range); int pos = parser.find_consecutive_frames(buf->get_buffer(), num_read, num_headers); in.setFilePointer(0, seek_beginning); return pos; } catch (...) { return -1; } }
void ebml_chapters_converter_c::write_xml(KaxChapters &chapters, mm_io_c &out) { document_cptr doc(new pugi::xml_document); doc->append_child(pugi::node_comment).set_value(" <!DOCTYPE Chapters SYSTEM \"matroskachapters.dtd\"> "); ebml_chapters_converter_c converter; converter.to_xml(chapters, doc); std::stringstream out_stream; doc->save(out_stream, " ", pugi::format_default | pugi::format_write_bom); out.puts(out_stream.str()); }
bool mpeg_es_reader_c::read_frame(M2VParser &parser, mm_io_c &in, int64_t max_size) { int bytes_probed; bytes_probed = 0; while (true) { int state; state = parser.GetState(); if (MPV_PARSER_STATE_NEED_DATA == state) { if ((max_size != -1) && (bytes_probed > max_size)) return false; int bytes_to_read = (parser.GetFreeBufferSpace() < READ_SIZE) ? parser.GetFreeBufferSpace() : READ_SIZE; unsigned char *buffer = new unsigned char[bytes_to_read]; int bytes_read = in.read(buffer, bytes_to_read); if (0 == bytes_read) { delete [] buffer; break; } bytes_probed += bytes_read; parser.WriteData(buffer, bytes_read); parser.SetEOS(); delete [] buffer; } else if (MPV_PARSER_STATE_FRAME == state) return true; else if ((MPV_PARSER_STATE_EOS == state) || (MPV_PARSER_STATE_ERROR == state)) return false; } return false; }
dts_reader_c::chunks_t dts_reader_c::scan_chunks(mm_io_c &in) { static auto s_debug = debugging_option_c{"dts_reader|dts_reader_chunks"}; auto chunks = chunks_t{}; try { auto const file_size = static_cast<uint64_t>(in.get_size()); in.setFilePointer(0, seek_beginning); auto type = static_cast<chunk_type_e>(in.read_uint64_be()); if (type != chunk_type_e::dtshdhdr) { chunks.emplace_back(chunk_type_e::strmdata, 0ull, file_size); return chunks; } auto next_chunk_start = 0ull; while (next_chunk_start < (file_size - 16)) { in.setFilePointer(next_chunk_start); type = static_cast<chunk_type_e>(in.read_uint64_be()); auto data_size = std::min<uint64_t>(in.read_uint64_be(), file_size - next_chunk_start - 16); chunks.emplace_back(type, next_chunk_start + 16, data_size); next_chunk_start = chunks.back().data_end; } } catch (mtx::mm_io::exception &) { } if (s_debug) for (auto const &chunk : chunks) mxinfo(boost::format("DTS chunk type %|1$16x| at %2% data size %3% data end %4%\n") % static_cast<uint64_t>(chunk.type) % (chunk.data_start - 16) % chunk.data_size % chunk.data_end); return chunks; }
void write_cuesheet(std::string file_name, KaxChapters &chapters, KaxTags &tags, int64_t tuid, mm_io_c &out) { if (chapters.ListSize() == 0) return; if (g_no_variable_data) file_name = "no-variable-data"; out.write_bom("UTF-8"); print_if_global("CATALOG", "CATALOG %1%\n"); // until 0.9.6 print_if_global("CATALOG_NUMBER", "CATALOG %1%\n"); // 0.9.7 and newer print_if_global("ARTIST", "PERFORMER \"%1%\"\n"); print_if_global("TITLE", "TITLE \"%1%\"\n"); print_if_global("DATE", "REM DATE \"%1%\"\n"); // until 0.9.6 print_if_global("DATE_RELEASED", "REM DATE \"%1%\"\n"); // 0.9.7 and newer print_if_global("DISCID", "REM DISCID %1%\n"); KaxTag *tag = find_tag_for_track(-1, tuid, 0, tags); if (NULL != tag) print_comments("", *tag, out); out.puts(boost::format("FILE \"%1%\" WAVE\n") % file_name); size_t i; for (i = 0; i < chapters.ListSize(); i++) { KaxChapterAtom &atom = *static_cast<KaxChapterAtom *>(chapters[i]); out.puts(boost::format(" TRACK %|1$02d| AUDIO\n") % (i + 1)); tag = find_tag_for_track(i + 1, tuid, get_chapter_uid(atom), tags); if (NULL == tag) continue; print_if_available("TITLE", " TITLE \"%1%\"\n"); print_if_available("ARTIST", " PERFORMER \"%1%\"\n"); print_if_available("ISRC", " ISRC %1%\n"); print_if_available("CDAUDIO_TRACK_FLAGS", " FLAGS %1%\n"); int k; for (k = 0; 100 > k; ++k) { int64_t temp_index = get_chapter_index(k, atom); if (-1 == temp_index) continue; out.puts(boost::format(" INDEX %|1$02d| %|2$02d|:%|3$02d|:%|4$02d|\n") % k % (temp_index / 1000000 / 1000 / 60) % ((temp_index / 1000000 / 1000) % 60) % irnd((double)(temp_index % 1000000000ll) * 75.0 / 1000000000.0)); } print_if_available("DATE", " REM DATE \"%1%\"\n"); // until 0.9.6 // 0.9.7 and newer: print_if_available("DATE_RELEASED", " REM DATE \"%1%\"\n"); print_if_available("GENRE", " REM GENRE \"%1%\"\n"); print_comments(" ", *tag, out); } }
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); }
uint32_t fourcc_c::read(mm_io_c &io, fourcc_c::byte_order_t byte_order) { return val(io.read_uint32_be(), byte_order); }
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); }