/* Reads an OGG page from the stream. Returns 0 if there are no more pages left, EMOREDATA otherwise. */ int ogm_reader_c::read_page(ogg_page *og) { int np, done, nread; unsigned char *buf; done = 0; while (!done) { np = ogg_sync_pageseek(&oy, og); // np == 0 means that there is not enough data for a complete page. if (0 >= np) { // np < 0 is the error case. Should not happen with local OGG files. if (0 > np) mxwarn_fn(m_ti.m_fname, Y("Could not find the next Ogg page. This indicates a damaged Ogg/Ogm file. Will try to continue.\n")); buf = (unsigned char *)ogg_sync_buffer(&oy, BUFFER_SIZE); if (!buf) mxerror_fn(m_ti.m_fname, Y("ogg_sync_buffer failed\n")); if (0 >= (nread = m_in->read(buf, BUFFER_SIZE))) return 0; ogg_sync_wrote(&oy, nread); } else // Alright, we have a page. done = 1; } // Here EMOREDATA actually indicates success - a page has been read. return FILE_STATUS_MOREDATA; }
void tta_reader_c::read_headers() { if (g_identifying) return; try { int tag_size = skip_id3v2_tag(*m_in); if (0 > tag_size) mxerror_fn(m_ti.m_fname, boost::format(Y("tta_reader: tag_size < 0 in the c'tor. %1%\n")) % BUGMSG); m_size -= tag_size; if (m_in->read(&header, sizeof(tta_file_header_t)) != sizeof(tta_file_header_t)) mxerror_fn(m_ti.m_fname, Y("The file header is too short.\n")); uint64_t seek_sum = m_in->getFilePointer() + 4 - tag_size; m_size -= id3_tag_present_at_end(*m_in); uint32_t seek_point; do { seek_point = m_in->read_uint32_le(); seek_sum += seek_point + 4; seek_points.push_back(seek_point); } while (seek_sum < m_size); mxverb(2, boost::format("tta: ch %1% bps %2% sr %3% dl %4% seek_sum %5% size %6% num %7%\n") % get_uint16_le(&header.channels) % get_uint16_le(&header.bits_per_sample) % get_uint32_le(&header.sample_rate) % get_uint32_le(&header.data_length) % seek_sum % m_size % seek_points.size()); if (seek_sum != m_size) mxerror_fn(m_ti.m_fname, Y("The seek table in this TTA file seems to be broken.\n")); m_in->skip(4); pos = 0; m_ti.m_id = 0; // ID for this track. } catch (...) { throw mtx::input::open_x(); } show_demuxer_info(); }
bool flac_reader_c::parse_file() { FLAC__StreamDecoderState state; flac_block_t block; uint64_t u, old_pos; int result, progress, old_progress; bool ok; m_in->setFilePointer(0); metadata_parsed = false; mxinfo(Y("+-> Parsing the FLAC file. This can take a LONG time.\n")); init_flac_decoder(); result = FLAC__stream_decoder_process_until_end_of_metadata(m_flac_decoder.get()); mxverb(2, boost::format("flac_reader: extract->metadata, result: %1%, mdp: %2%, num blocks: %3%\n") % result % metadata_parsed % blocks.size()); if (!metadata_parsed) mxerror_fn(m_ti.m_fname, Y("No metadata block found. This file is broken.\n")); FLAC__stream_decoder_get_decode_position(m_flac_decoder.get(), &u); block.type = FLAC_BLOCK_TYPE_HEADERS; block.filepos = 0; block.len = u; old_pos = u; blocks.push_back(block); mxverb(2, boost::format("flac_reader: headers: block at %1% with size %2%\n") % block.filepos % block.len); old_progress = -5; ok = FLAC__stream_decoder_skip_single_frame(m_flac_decoder.get()); while (ok) { state = FLAC__stream_decoder_get_state(m_flac_decoder.get()); progress = m_in->getFilePointer() * 100 / m_size; if ((progress - old_progress) >= 5) { mxinfo(boost::format(Y("+-> Pre-parsing FLAC file: %1%%%%2%")) % progress % "\r"); old_progress = progress; } if (FLAC__stream_decoder_get_decode_position(m_flac_decoder.get(), &u) && (u != old_pos)) { block.type = FLAC_BLOCK_TYPE_DATA; block.filepos = old_pos; block.len = u - old_pos; old_pos = u; blocks.push_back(block); mxverb(2, boost::format("flac_reader: skip/decode frame, block at %1% with size %2%\n") % block.filepos % block.len); } if (state > FLAC__STREAM_DECODER_READ_FRAME) break; ok = FLAC__stream_decoder_skip_single_frame(m_flac_decoder.get()); } if (100 != old_progress) mxinfo(Y("+-> Pre-parsing FLAC file: 100%\n")); else mxinfo("\n"); if ((blocks.size() == 0) || (blocks[0].type != FLAC_BLOCK_TYPE_HEADERS)) mxerror(Y("flac_reader: Could not read all header packets.\n")); m_in->setFilePointer(0); blocks[0].len -= 4; blocks[0].filepos = 4; return metadata_parsed; }
/* The page is the beginning of a new stream. Check the contents for known stream headers. If it is a known stream and the user has requested that it should be extracted then allocate a new packetizer based on the stream type and store the needed data in a new ogm_demuxer_c. */ void ogm_reader_c::handle_new_stream(ogg_page *og) { ogg_stream_state os; ogg_packet op; if (ogg_stream_init(&os, ogg_page_serialno(og))) { mxwarn_fn(m_ti.m_fname, boost::format(Y("ogg_stream_init for stream number %1% failed. Will try to continue and ignore this stream.\n")) % sdemuxers.size()); return; } // Read the first page and get its first packet. ogg_stream_pagein(&os, og); ogg_stream_packetout(&os, &op); ogm_demuxer_c *dmx = nullptr; /* * Check the contents for known stream headers. This one is the * standard Vorbis header. */ if ((7 <= op.bytes) && !strncmp((char *)&op.packet[1], "vorbis", 6)) dmx = new ogm_a_vorbis_demuxer_c(this); else if ((7 <= op.bytes) && !strncmp((char *)&op.packet[1], "theora", 6)) dmx = new ogm_v_theora_demuxer_c(this); else if ((8 <= op.bytes) && !memcmp(&op.packet[1], "kate\0\0\0", 7)) dmx = new ogm_s_kate_demuxer_c(this); // FLAC else if ( ((4 <= op.bytes) && !strncmp(reinterpret_cast<char *>(&op.packet[0]), "fLaC", 4)) || ((5 <= op.bytes) && !strncmp(reinterpret_cast<char *>(&op.packet[1]), "FLAC", 4) && (0x7f == op.packet[0]))) { #if !defined(HAVE_FLAC_FORMAT_H) if (demuxing_requested('a', sdemuxers.size())) mxerror_fn(m_ti.m_fname, Y("mkvmerge has not been compiled with FLAC support but handling of this stream has been requested.\n")); else { dmx = new ogm_demuxer_c(this); dmx->stype = OGM_STREAM_TYPE_A_FLAC; dmx->in_use = true; } #else dmx = new ogm_a_flac_demuxer_c(this, 0x7f == op.packet[0] ? ofm_post_1_1_1 : ofm_pre_1_1_1); #endif } else if ((static_cast<size_t>(op.bytes) >= sizeof(vp8_ogg_header_t)) && (0x4f == op.packet[0]) && (get_uint32_be(&op.packet[1]) == 0x56503830)) dmx = new ogm_v_vp8_demuxer_c(this, op); else if (((*op.packet & PACKET_TYPE_BITS ) == PACKET_TYPE_HEADER) && (op.bytes >= ((int)sizeof(stream_header) + 1))) { // The new stream headers introduced by OggDS (see ogmstreams.h). stream_header *sth = (stream_header *)(op.packet + 1); char buf[5]; buf[4] = 0; if (!strncmp(sth->streamtype, "video", 5)) { memcpy(buf, (char *)sth->subtype, 4); if (mpeg4::p10::is_avc_fourcc(buf) && !hack_engaged(ENGAGE_ALLOW_AVC_IN_VFW_MODE)) dmx = new ogm_v_avc_demuxer_c(this); else dmx = new ogm_v_mscomp_demuxer_c(this); } else if (!strncmp(sth->streamtype, "audio", 5)) { memcpy(buf, (char *)sth->subtype, 4); uint32_t codec_id = strtol(buf, (char **)nullptr, 16); if (0x0001 == codec_id) dmx = new ogm_a_pcm_demuxer_c(this); else if ((0x0050 == codec_id) || (0x0055 == codec_id)) dmx = new ogm_a_mp3_demuxer_c(this); else if (0x2000 == codec_id) dmx = new ogm_a_ac3_demuxer_c(this); else if (0x00ff == codec_id) dmx = new ogm_a_aac_demuxer_c(this); else mxwarn_fn(m_ti.m_fname, boost::format(Y("Unknown audio stream type 0x%|1$04x|. Stream ID %2% will be ignored.\n")) % codec_id % sdemuxers.size()); } else if (!strncmp(sth->streamtype, "text", 4)) dmx = new ogm_s_text_demuxer_c(this); } /* * The old OggDS headers (see MPlayer's source, libmpdemux/demux_ogg.c) * are not supported. */ if (!dmx) dmx = new ogm_demuxer_c(this); std::string type = dmx->get_type(); dmx->serialno = ogg_page_serialno(og); dmx->track_id = sdemuxers.size(); dmx->in_use = (type != "unknown") && demuxing_requested(type[0], dmx->track_id); dmx->packet_data.push_back(memory_cptr(new memory_c((unsigned char *)safememdup(op.packet, op.bytes), op.bytes, true))); memcpy(&dmx->os, &os, sizeof(ogg_stream_state)); sdemuxers.push_back(ogm_demuxer_cptr(dmx)); dmx->initialize(); }