From e5fb1c3690392c5d74ef87ea2ba9531a9cbcbd31 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Wed, 27 Jun 2007 13:51:21 +0000 Subject: [PATCH] The OGM reader now uses the AVC/h.264 video packetizer for AVC/h.264 tracks so that the aspect ratio can be extracted from it. The OGM reader uses the OGM's timestamps for video tracks. Before it would just use the current frame number multiplied by the FPS. --- ChangeLog | 10 ++++ src/input/r_ogm.cpp | 114 ++++++++++++++++++++++++++++++++++++++------ src/input/r_ogm.h | 34 +++++++------ 3 files changed, 128 insertions(+), 30 deletions(-) diff --git a/ChangeLog b/ChangeLog index a6de3142e..60f2f4d3b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2007-06-27 Moritz Bunkus + + * mkvmerge: bug fix: The OGM reader uses the OGM's timestamps for + video tracks. Before it would just use the current frame number + multiplied by the FPS. + + * mkvmerge: enhancement: The OGM reader now uses the AVC/h.264 + video packetizer for AVC/h.264 tracks so that the aspect ratio can + be extracted from it. + 2007-06-25 Moritz Bunkus * mkvmerge: bug fixes: Fixed a couple of memory leaks. diff --git a/src/input/r_ogm.cpp b/src/input/r_ogm.cpp index ca675e6b4..15d8f7dcf 100644 --- a/src/input/r_ogm.cpp +++ b/src/input/r_ogm.cpp @@ -42,6 +42,7 @@ extern "C" { // for BITMAPINFOHEADER #include "chapters.h" #include "common.h" #include "commonebml.h" +#include "hacks.h" #include "iso639.h" #include "matroska.h" #include "ogmstreams.h" @@ -49,6 +50,7 @@ extern "C" { // for BITMAPINFOHEADER #include "pr_generic.h" #include "p_aac.h" #include "p_ac3.h" +#include "p_avc.h" #if defined(HAVE_FLAC_FORMAT_H) #include "p_flac.h" #endif @@ -446,15 +448,37 @@ ogm_reader_c::create_packetizer(int64_t tid) { fps = (double)10000000.0 / get_uint64_le(&sth->time_unit); width = get_uint32_le(&sth->sh.video.width); height = get_uint32_le(&sth->sh.video.height); + if (mpeg4::p2::is_fourcc(sth->subtype)) { ptzr = new mpeg4_p2_video_packetizer_c(this, fps, width, height, false, ti); mxinfo(FMT_TID "Using the MPEG-4 part 2 video output module.\n", ti.fname.c_str(), (int64_t)tid); + + } else if (dmx->is_avc && !hack_engaged(ENGAGE_ALLOW_AVC_IN_VFW_MODE)) { + try { + ti.private_data = NULL; + ti.private_size = 0; + dmx->default_duration = 100 * get_uint64_le(&sth->time_unit); + memory_cptr avcc = extract_avcc(dmx, tid); + + mpeg4_p10_es_video_packetizer_c *vptzr = new mpeg4_p10_es_video_packetizer_c(this, avcc, width, height, ti); + + vptzr->enable_timecode_generation(false); + vptzr->set_track_default_duration(dmx->default_duration); + + if (verbose) + mxinfo(FMT_TID "Using the MPEG-4 part 10 ES video output module.\n", ti.fname.c_str(), (int64_t)tid); + + ptzr = vptzr; + + } catch (...) { + mxerror(FMT_TID "Could not extract the decoder specific config data (AVCC) from this AVC/h.264 track.\n", ti.fname.c_str(), (int64_t)tid); + } + } else { ptzr = new video_packetizer_c(this, NULL, fps, width, height, ti); - mxinfo(FMT_TID "Using the video output module.\n", ti.fname.c_str(), - (int64_t)tid); + mxinfo(FMT_TID "Using the video output module.\n", ti.fname.c_str(), (int64_t)tid); } ti.private_data = NULL; @@ -754,6 +778,15 @@ ogm_reader_c::handle_new_stream(ogg_page *og) { video_fps = 10000000.0 / (float)get_uint64_le(&sth->time_unit); dmx->in_use = true; + sth = (stream_header *)&op.packet[1]; + memcpy(buf, (char *)sth->subtype, 4); + buf[4] = 0; + + if (mpeg4::p10::is_avc_fourcc(buf)) { + dmx->is_avc = true; + dmx->num_non_header_packets = 3; + } + return; } @@ -918,13 +951,18 @@ ogm_reader_c::process_page(ogg_page *og) { ((*op.packet & 3) != PACKET_TYPE_COMMENT)) { if (dmx->stype == OGM_STREAM_TYPE_VIDEO) { - memory_c *mem = new memory_c(&op.packet[duration_len + 1], - op.bytes - 1 - duration_len, false); - PTZR(dmx->ptzr)-> - process(new packet_t(mem, -1, -1, - (*op.packet & PACKET_IS_SYNCPOINT ? - VFT_IFRAME : VFT_PFRAMEAUTOMATIC))); - dmx->units_processed += (duration_len > 0 ? duration : 1); + if (!duration_len || !duration) + duration = 1; + + int64_t pos = ogg_page_granulepos(og); + + if (!dmx->units_processed && (1 == pos)) + dmx->first_granulepos = 1; + pos -= dmx->first_granulepos; + + memory_c *mem = new memory_c(&op.packet[duration_len + 1], op.bytes - 1 - duration_len, false); + PTZR(dmx->ptzr)->process(new packet_t(mem, pos * dmx->default_duration, (int64_t)duration * dmx->default_duration)); + dmx->units_processed += duration; } else if (dmx->stype == OGM_STREAM_TYPE_TEXT) { dmx->units_processed++; @@ -1019,21 +1057,28 @@ ogm_reader_c::process_header_packets(ogm_demuxer_t *dmx) { is_header_packet = op.packet[0] & 1; if (!is_header_packet) { + if (dmx->nh_packet_data.size() != dmx->num_non_header_packets) { + memory_c *mem = new memory_c(safememdup(op.packet, op.bytes), op.bytes, true); + dmx->nh_packet_data.push_back(memory_cptr(mem)); + + continue; + } + mxwarn("ogm_reader: Missing header/comment packets for stream %d in " "'%s'. This file is broken but should be muxed correctly. If " - "not please contact the author Moritz Bunkus " - ".\n", dmx->serialno, ti.fname.c_str()); + "not please contact the author Moritz Bunkus .\n", + dmx->serialno, ti.fname.c_str()); dmx->headers_read = true; ogg_stream_reset(&dmx->os); return; } - memory_c *mem = new memory_c((unsigned char *) - safememdup(op.packet, op.bytes), - op.bytes, true); + + memory_c *mem = new memory_c(safememdup(op.packet, op.bytes), op.bytes, true); dmx->packet_data.push_back(memory_cptr(mem)); } - if (dmx->packet_data.size() == dmx->num_header_packets) + if ( (dmx->packet_data.size() == dmx->num_header_packets) + && (dmx->nh_packet_data.size() >= dmx->num_non_header_packets)) dmx->headers_read = true; } @@ -1299,3 +1344,42 @@ ogm_reader_c::add_available_track_ids() { available_track_ids.push_back(i); } +memory_cptr +ogm_reader_c::extract_avcc(ogm_demuxer_t *dmx, + int64_t tid) { + avc_es_parser_c parser; + + parser.ignore_nalu_size_length_errors(); + if (map_has_key(ti.nalu_size_lengths, tid)) + parser.set_nalu_size_length(ti.nalu_size_lengths[tid]); + else if (map_has_key(ti.nalu_size_lengths, -1)) + parser.set_nalu_size_length(ti.nalu_size_lengths[-1]); + + unsigned char *private_data = dmx->packet_data[0]->get() + 1 + sizeof(stream_header); + int private_size = dmx->packet_data[0]->get_size() - 1 - sizeof(stream_header); + + while (4 < private_size) { + if (get_uint32_be(private_data) == 0x00000001) { + parser.add_bytes(private_data, private_size); + break; + } + + ++private_data; + --private_size; + } + + vector::iterator packet(dmx->nh_packet_data.begin()); + + while (packet != dmx->nh_packet_data.end()) { + if ((*packet)->get_size()) { + parser.add_bytes((*packet)->get(), (*packet)->get_size()); + if (parser.headers_parsed()) + return parser.get_avcc(); + } + + ++packet; + } + + throw false; +} + diff --git a/src/input/r_ogm.h b/src/input/r_ogm.h index 8766c69b2..0fd97d274 100644 --- a/src/input/r_ogm.h +++ b/src/input/r_ogm.h @@ -32,16 +32,17 @@ #include "pr_generic.h" #include "theora_common.h" -#define OGM_STREAM_TYPE_UNKNOWN 0 -#define OGM_STREAM_TYPE_VORBIS 1 -#define OGM_STREAM_TYPE_VIDEO 2 -#define OGM_STREAM_TYPE_PCM 3 -#define OGM_STREAM_TYPE_MP3 4 -#define OGM_STREAM_TYPE_AC3 5 -#define OGM_STREAM_TYPE_TEXT 6 -#define OGM_STREAM_TYPE_FLAC 7 -#define OGM_STREAM_TYPE_AAC 8 -#define OGM_STREAM_TYPE_THEORA 9 +#define OGM_STREAM_TYPE_UNKNOWN 0 +#define OGM_STREAM_TYPE_VORBIS 1 +#define OGM_STREAM_TYPE_VIDEO 2 +#define OGM_STREAM_TYPE_PCM 3 +#define OGM_STREAM_TYPE_MP3 4 +#define OGM_STREAM_TYPE_AC3 5 +#define OGM_STREAM_TYPE_TEXT 6 +#define OGM_STREAM_TYPE_FLAC 7 +#define OGM_STREAM_TYPE_AAC 8 +#define OGM_STREAM_TYPE_THEORA 9 +#define OGM_STREAM_TYPE_VIDEO_AVC 10 #if defined(HAVE_FLAC_FORMAT_H) class flac_header_extractor_c { @@ -69,7 +70,7 @@ struct ogm_demuxer_t { int ptzr; int stype, serialno, eos; int units_processed, vorbis_rate; - int num_header_packets; + int num_header_packets, num_non_header_packets; bool headers_read; string language, title; vector packet_data, nh_packet_data; @@ -77,16 +78,18 @@ struct ogm_demuxer_t { flac_header_extractor_c *fhe; int flac_header_packets, channels, bits_per_sample; #endif - int64_t last_granulepos, last_keyframe_number; + int64_t first_granulepos, last_granulepos, last_keyframe_number, default_duration; bool in_use; theora_identification_header_t theora; + bool is_avc; + ogm_demuxer_t(): ptzr(-1), stype(0), serialno(0), eos(0), units_processed(0), - vorbis_rate(0), num_header_packets(2), headers_read(false), - last_granulepos(0), last_keyframe_number(-1), - in_use(false) { + vorbis_rate(0), num_header_packets(2), num_non_header_packets(0), headers_read(false), + first_granulepos(0), last_granulepos(0), last_keyframe_number(-1), default_duration(0), + in_use(false), is_avc(false) { memset(&os, 0, sizeof(ogg_stream_state)); } }; @@ -124,6 +127,7 @@ private: virtual void process_header_page(ogg_page *pg); virtual void process_header_packets(ogm_demuxer_t *dmx); virtual void handle_stream_comments(); + virtual memory_cptr extract_avcc(ogm_demuxer_t *dmx, int64_t tid); };