diff --git a/src/common/mpeg4_common.cpp b/src/common/mpeg4_common.cpp index 56608d2ee..c5d3d1edd 100644 --- a/src/common/mpeg4_common.cpp +++ b/src/common/mpeg4_common.cpp @@ -383,6 +383,68 @@ mpeg1_2_extract_fps_idx(const unsigned char *buffer, return buffer[idx] & 0x0f; } +/** \brief Extract the aspect ratio from a MPEG video sequence header + + This function looks for a MPEG sequence header in a buffer containing + a MPEG1 or MPEG2 video frame. If such a header is found its + aspect ratio is extracted and returned. + + \param buffer The buffer to search for the header. + \param size The buffer size. + + \return \c true if a MPEG sequence header was found and \c false otherwise. +*/ +bool +mpeg1_2_extract_ar(const unsigned char *buffer, + int size, + float &ar) { + uint32_t marker; + int idx; + + mxverb(3, "mpeg_video_ar: start search in %d bytes\n", size); + if (size < 8) { + mxverb(3, "mpeg_video_ar: sequence header too small\n"); + return -1; + } + marker = get_uint32_be(buffer); + idx = 4; + while ((idx < size) && (marker != MPEGVIDEO_SEQUENCE_START_CODE)) { + marker <<= 8; + marker |= buffer[idx]; + idx++; + } + if (idx >= size) { + mxverb(3, "mpeg_video_ar: no sequence header start code found\n"); + return -1; + } + + mxverb(3, "mpeg_video_ar: found sequence header start code at %d\n", + idx - 4); + idx += 3; // width and height + if (idx >= size) { + mxverb(3, "mpeg_video_ar: sequence header too small\n"); + return -1; + } + + switch (buffer[idx] & 0xf0) { + case MPEGVIDEO_AR_1_1: + ar = 1.0f; + break; + case MPEGVIDEO_AR_4_3: + ar = 4.0f / 3.0f; + break; + case MPEGVIDEO_AR_16_9: + ar = 16.0f / 9.0f; + break; + case MPEGVIDEO_AR_2_21: + ar = 2.21f; + break; + default: + ar = -1.0f; + } + return true; +} + /** \brief Get the number of frames per second Converts the index returned by ::mpeg_video_extract_fps_idx to a number. diff --git a/src/common/mpeg4_common.h b/src/common/mpeg4_common.h index 1a9374fb2..56dac5e0c 100644 --- a/src/common/mpeg4_common.h +++ b/src/common/mpeg4_common.h @@ -56,6 +56,22 @@ /** MPEG-1/-2 frame rate: 60 frames per second */ #define MPEGVIDEO_FPS_60 0x08 +/** MPEG-1/-2 aspect ratio 1:1 */ +#define MPEGVIDEO_AR_1_1 0x10 +/** MPEG-1/-2 aspect ratio 4:3 */ +#define MPEGVIDEO_AR_4_3 0x20 +/** MPEG-1/-2 aspect ratio 16:9 */ +#define MPEGVIDEO_AR_16_9 0x30 +/** MPEG-1/-2 aspect ratio 2.21 */ +#define MPEGVIDEO_AR_2_21 0x40 + +#define IS_MPEG4_L2_FOURCC(s) \ + (!strncasecmp((s), "DIVX", 4) || !strncasecmp((s), "XVID", 4) || \ + !strncasecmp((s), "DX5", 3)) +#define IS_MPEG4_L2_CODECID(s) \ + (((s) == MKV_V_MPEG4_SP) || ((s) == MKV_V_MPEG4_AP) || \ + ((s) == MKV_V_MPEG4_ASP)) + enum mpeg_video_type_e { MPEG_VIDEO_NONE = 0, MPEG_VIDEO_V1, @@ -73,7 +89,7 @@ enum mpeg_video_type_e { such packed frames can be analyzed. The results are stored in these structures: one structure for one frame in the analyzed chunk. */ -typedef struct { +struct video_frame_t { /** The beginning of the frame data. This is a pointer into an existing buffer handed over to ::mpeg4_find_frame_types. */ unsigned char *data; @@ -97,7 +113,11 @@ typedef struct { \link video_frame_t::timecode timecode \endlink. This value is only set for B frames. */ int64_t fref; -} video_frame_t; + + video_frame_t(): + data(NULL), size(0), pos(0), type('?'), priv(NULL), + timecode(0), duration(0), bref(0), fref(0) {}; +}; bool MTX_DLL_API mpeg4_extract_par(const unsigned char *buffer, int size, uint32_t &par_num, uint32_t &par_den); @@ -110,5 +130,7 @@ bool MTX_DLL_API mpeg4_l10_extract_par(const uint8_t *buffer, int buf_size, int MTX_DLL_API mpeg1_2_extract_fps_idx(const unsigned char *buffer, int size); double MTX_DLL_API mpeg1_2_get_fps(int idx); +bool MTX_DLL_API mpeg1_2_extract_ar(const unsigned char *buffer, int size, + float &ar); #endif /* __MPEG4_COMMON_H */ diff --git a/src/input/r_avi.cpp b/src/input/r_avi.cpp index 6ed1ca786..a23bcd50b 100644 --- a/src/input/r_avi.cpp +++ b/src/input/r_avi.cpp @@ -172,14 +172,27 @@ avi_reader_c::create_packetizer(int64_t tid) { if (ti->private_data != NULL) ti->private_size = get_uint32_le(&avi->bitmap_info_header->bi_size); ti->id = 0; // ID for the video track. - vptzr = add_packetizer(new video_packetizer_c(this, NULL, - AVI_frame_rate(avi), - AVI_video_width(avi), - AVI_video_height(avi), - false, ti)); - if (verbose) - mxinfo(FMT_TID "Using the video output module for the video track.\n", - ti->fname.c_str(), (int64_t)0); + if (is_divx == RAVI_MPEG4) { + vptzr = + add_packetizer(new mpeg4_l2_video_packetizer_c(this, + AVI_frame_rate(avi), + AVI_video_width(avi), + AVI_video_height(avi), + false, + ti)); + if (verbose) + mxinfo(FMT_TID "Using the MPEG-4 layer 2 video output module for " + "this track.\n", ti->fname.c_str(), (int64_t)0); + } else { + vptzr = add_packetizer(new video_packetizer_c(this, NULL, + AVI_frame_rate(avi), + AVI_video_width(avi), + AVI_video_height(avi), + ti)); + if (verbose) + mxinfo(FMT_TID "Using the video output module for the video track.\n", + ti->fname.c_str(), (int64_t)0); + } } if (tid == 0) return; diff --git a/src/input/r_matroska.cpp b/src/input/r_matroska.cpp index 66c5e7468..7f8be1c01 100644 --- a/src/input/r_matroska.cpp +++ b/src/input/r_matroska.cpp @@ -1591,15 +1591,72 @@ kax_reader_c::create_packetizer(int64_t tid) { if (starts_with(t->codec_id, "V_MPEG4", 7) || (t->codec_id == MKV_V_MSCOMP) || starts_with(t->codec_id, "V_REAL", 6) || - (t->codec_id == MKV_V_QUICKTIME)) { - mxinfo(FMT_TID "Using the video output module.\n", ti->fname.c_str(), - (int64_t)t->tnum); - t->ptzr = add_packetizer(new video_packetizer_c(this, - t->codec_id.c_str(), - t->v_frate, - t->v_width, - t->v_height, - t->v_bframes, nti)); + (t->codec_id == MKV_V_QUICKTIME) || + (t->codec_id == MKV_V_MPEG1) || + (t->codec_id == MKV_V_MPEG2)) { + const char *fourcc; + + if ((t->codec_id == MKV_V_MSCOMP) && + (t->private_data != NULL) && + (t->private_size >= sizeof(alBITMAPINFOHEADER))) + fourcc = (const char *) + &((alBITMAPINFOHEADER *)t->private_data)->bi_compression; + else + fourcc = NULL; + + if ((t->codec_id == MKV_V_MPEG1) || (t->codec_id == MKV_V_MPEG2)) { + int version; + + version = t->codec_id[6] - '0'; + mxinfo(FMT_TID "Using the MPEG-%d video output module.\n", + ti->fname.c_str(), (int64_t)t->tnum, version); + t->ptzr = + add_packetizer(new mpeg1_2_video_packetizer_c(this, + version, + t->v_frate, + t->v_width, + t->v_height, + t->v_dwidth, + t->v_dheight, + true, nti)); + + } else if (IS_MPEG4_L2_CODECID(t->codec_id) || + ((fourcc != NULL) && IS_MPEG4_L2_FOURCC(fourcc))) { + bool is_native; + + mxinfo(FMT_TID "Using the MPEG-4 layer 2 video output module.\n", + ti->fname.c_str(), (int64_t)t->tnum); + is_native = IS_MPEG4_L2_CODECID(t->codec_id); + t->ptzr = + add_packetizer(new mpeg4_l2_video_packetizer_c(this, + t->v_frate, + t->v_width, + t->v_height, + is_native, + nti)); + + } else if (t->codec_id == MKV_V_MPEG4_AVC) { + mxinfo(FMT_TID "Using the MPEG-4 layer 10 (AVC) video output " + "module.\n", ti->fname.c_str(), (int64_t)t->tnum); + t->ptzr = + add_packetizer(new mpeg4_l10_video_packetizer_c(this, + t->v_frate, + t->v_width, + t->v_height, + nti)); + + } else { + mxinfo(FMT_TID "Using the video output module.\n", + ti->fname.c_str(), (int64_t)t->tnum); + t->ptzr = + add_packetizer(new video_packetizer_c(this, + t->codec_id.c_str(), + t->v_frate, + t->v_width, + t->v_height, + nti)); + } + if (!PTZR(t->ptzr)->ti->aspect_ratio_given) { // The user hasn't set it. if (t->v_dwidth != 0) diff --git a/src/input/r_mpeg.cpp b/src/input/r_mpeg.cpp index 687b3832a..87cdbc4f3 100644 --- a/src/input/r_mpeg.cpp +++ b/src/input/r_mpeg.cpp @@ -149,9 +149,9 @@ mpeg_es_reader_c::create_packetizer(int64_t) { if (NPTZR() != 0) return; - add_packetizer(new mpeg_12_video_packetizer_c(this, version, frame_rate, + add_packetizer(new mpeg1_2_video_packetizer_c(this, version, frame_rate, width, height, dwidth, dheight, - ti)); + false, ti)); mxinfo(FMT_TID "Using the MPEG-1/2 video output module.\n", ti->fname.c_str(), (int64_t)0); @@ -801,15 +801,16 @@ mpeg_ps_reader_c::create_packetizer(int64_t id) { } else { // if (track->type == 'a') if ((track->fourcc == FOURCC('m', 'p', 'g', '1')) || (track->fourcc == FOURCC('m', 'p', 'g', '2'))) { - mpeg_12_video_packetizer_c *ptzr; + mpeg1_2_video_packetizer_c *ptzr; ti->private_data = track->raw_seq_hdr; ti->private_size = track->raw_seq_hdr_size; ptzr = - new mpeg_12_video_packetizer_c(this, track->v_version, + new mpeg1_2_video_packetizer_c(this, track->v_version, track->v_frame_rate, track->v_width, track->v_height, - track->v_dwidth, track->v_dheight, ti); + track->v_dwidth, track->v_dheight, + false, ti); track->ptzr = add_packetizer(ptzr); ti->private_data = NULL; ti->private_size = 0; diff --git a/src/input/r_ogm.cpp b/src/input/r_ogm.cpp index 387ef9e50..2c47d798f 100644 --- a/src/input/r_ogm.cpp +++ b/src/input/r_ogm.cpp @@ -429,7 +429,7 @@ ogm_reader_c::create_packetizer(int64_t tid) { get_uint64_le(&sth->time_unit), get_uint32_le(&sth->sh.video.width), get_uint32_le(&sth->sh.video.height), - false, ti); + ti); mxinfo(FMT_TID "Using the video output module.\n", ti->fname.c_str(), (int64_t)tid); diff --git a/src/input/r_qtmp4.cpp b/src/input/r_qtmp4.cpp index 18eb1d1b0..6b72441c6 100644 --- a/src/input/r_qtmp4.cpp +++ b/src/input/r_qtmp4.cpp @@ -1381,21 +1381,27 @@ qtmp4_reader_c::create_packetizer(int64_t tid) { ti->private_size = sizeof(alBITMAPINFOHEADER); ti->private_data = (unsigned char *)bih; dmx->ptzr = - add_packetizer(new video_packetizer_c(this, MKV_V_MSCOMP, 0.0, - dmx->v_width, dmx->v_height, - false, ti)); + add_packetizer(new mpeg4_l2_video_packetizer_c(this, 0.0, + dmx->v_width, + dmx->v_height, true, + ti)); safefree(bih); ti->private_data = NULL; + mxinfo(FMT_TID "Using the MPEG-4 layer 2 video output module.\n", + ti->fname.c_str(), (int64_t)dmx->id); } else if (!strncasecmp(dmx->fourcc, "mpg1", 4) || !strncasecmp(dmx->fourcc, "mpg2", 4)) { - string codec_id; + int version; - codec_id = mxsprintf("V_MPEG%c", dmx->fourcc[3]); + version = dmx->fourcc[3] - '0'; dmx->ptzr = - add_packetizer(new video_packetizer_c(this, codec_id.c_str(), - -1.0, dmx->v_width, - dmx->v_height, false, ti)); + add_packetizer(new mpeg1_2_video_packetizer_c(this, version, + -1.0, dmx->v_width, + dmx->v_height, 0, 0, + false, ti)); + mxinfo(FMT_TID "Using the MPEG-%d video output module.\n", + ti->fname.c_str(), (int64_t)dmx->id, version); } else if (!strncasecmp(dmx->fourcc, "avc1", 4)) { double fps; @@ -1449,15 +1455,17 @@ qtmp4_reader_c::create_packetizer(int64_t tid) { ti->private_size = dmx->priv_size; ti->private_data = dmx->priv; dmx->ptzr = - add_packetizer(new video_packetizer_c(this, MKV_V_MPEG4_AVC, fps, - dmx->v_width, dmx->v_height, - false, ti)); + add_packetizer(new mpeg4_l10_video_packetizer_c(this, fps, + dmx->v_width, + dmx->v_height, ti)); ti->private_data = NULL; if (hack_engaged(ENGAGE_AVC_USE_BFRAMES)) dmx->avc_use_bframes = true; else PTZR(dmx->ptzr)->relaxed_timecode_checking = true; + mxinfo(FMT_TID "Using the MPEG-4 layer 10 (AVC) video output " + "module.\n", ti->fname.c_str(), (int64_t)dmx->id); } else { ti->private_size = dmx->v_stsd_size; @@ -1465,11 +1473,11 @@ qtmp4_reader_c::create_packetizer(int64_t tid) { dmx->ptzr = add_packetizer(new video_packetizer_c(this, MKV_V_QUICKTIME, 0.0, dmx->v_width, dmx->v_height, - false, ti)); + ti)); ti->private_data = NULL; + mxinfo(FMT_TID "Using the video output module (FourCC: %.4s).\n", + ti->fname.c_str(), (int64_t)dmx->id, dmx->fourcc); } - mxinfo(FMT_TID "Using the video output module (FourCC: %.4s).\n", - ti->fname.c_str(), (int64_t)dmx->id, dmx->fourcc); } else { if (!strncasecmp(dmx->fourcc, "QDMC", 4) || diff --git a/src/input/r_real.cpp b/src/input/r_real.cpp index 3100f16aa..1132d7e2f 100644 --- a/src/input/r_real.cpp +++ b/src/input/r_real.cpp @@ -260,8 +260,7 @@ real_reader_c::create_packetizer(int64_t tid) { mxprints(buffer, "V_REAL/%s", dmx->fourcc); dmx->ptzr = add_packetizer(new video_packetizer_c(this, buffer, dmx->fps, - dmx->width, dmx->height, false, - ti)); + dmx->width, dmx->height, ti)); if ((dmx->fourcc[0] != 'R') || (dmx->fourcc[1] != 'V') || (dmx->fourcc[2] != '4') || (dmx->fourcc[3] != '0')) dmx->rv_dimensions = true; diff --git a/src/output/p_video.cpp b/src/output/p_video.cpp index 0580c055d..fa8a3a32f 100644 --- a/src/output/p_video.cpp +++ b/src/output/p_video.cpp @@ -32,82 +32,42 @@ extern "C" { using namespace libmatroska; -video_packetizer_c::video_packetizer_c(generic_reader_c *nreader, - const char *ncodec_id, - double nfps, - int nwidth, - int nheight, - bool nbframes, - track_info_c *nti) - throw (error_c) : generic_packetizer_c(nreader, nti) { - char *fourcc; +video_packetizer_c::video_packetizer_c(generic_reader_c *_reader, + const char *_codec_id, + double _fps, + int _width, + int _height, + track_info_c *_ti): + generic_packetizer_c(_reader, _ti), + fps(_fps), width(_width), height(_height), + frames_output(0), ref_timecode(-1), duration_shift(0) { - fps = nfps; - width = nwidth; - height = nheight; - frames_output = 0; - bframes = nbframes; - ref_timecode = -1; if (get_cue_creation() == CUE_STRATEGY_UNSPECIFIED) set_cue_creation(CUE_STRATEGY_IFRAMES); - duration_shift = 0; - bref_frame.type = '?'; - fref_frame.type = '?'; - aspect_ratio_extracted = false; set_track_type(track_video); - mpeg_video = MPEG_VIDEO_NONE; - if ((ti->private_data != NULL) && - (ti->private_size >= sizeof(alBITMAPINFOHEADER))) { - fourcc = (char *)&((alBITMAPINFOHEADER *)ti->private_data)->bi_compression; - if (!strncasecmp(fourcc, "DIVX", 4) || - !strncasecmp(fourcc, "XVID", 4) || - !strncasecmp(fourcc, "DX5", 3)) - mpeg_video = MPEG_VIDEO_V4_LAYER_2; - } - if ((mpeg_video == MPEG_VIDEO_NONE) && (ncodec_id != NULL) && - !strncmp(ncodec_id, MKV_V_MPEG4_SP, strlen(MKV_V_MPEG4_SP) - 2)) { - if (!strcmp(ncodec_id, MKV_V_MPEG4_AVC)) - mpeg_video = MPEG_VIDEO_V4_LAYER_10; - else - mpeg_video = MPEG_VIDEO_V4_LAYER_2; - } - - if ((mpeg_video == MPEG_VIDEO_NONE) && (ncodec_id != NULL) && - (!strcmp(ncodec_id, MKV_V_MPEG1) || !strcmp(ncodec_id, MKV_V_MPEG2))) - mpeg_video = (ncodec_id[6] == '1') ? MPEG_VIDEO_V1 : MPEG_VIDEO_V2; - - if ((mpeg_video == MPEG_VIDEO_V4_LAYER_2) && - hack_engaged(ENGAGE_NATIVE_MPEG4)) - set_codec_id(MKV_V_MPEG4_ASP); - else if (ncodec_id != NULL) - set_codec_id(ncodec_id); + if (_codec_id != NULL) + set_codec_id(_codec_id); else set_codec_id(MKV_V_MSCOMP); - if (((mpeg_video != MPEG_VIDEO_V4_LAYER_2) || - !hack_engaged(ENGAGE_NATIVE_MPEG4)) && - (hcodec_id != "") && (hcodec_id == MKV_V_MSCOMP) && + set_codec_private(ti->private_data, ti->private_size); + check_fourcc(); +} + +void +video_packetizer_c::check_fourcc() { + if ((hcodec_id == MKV_V_MSCOMP) && (ti->private_data != NULL) && (ti->private_size >= sizeof(alBITMAPINFOHEADER)) && (ti->fourcc[0] != 0)) memcpy(&((alBITMAPINFOHEADER *)ti->private_data)->bi_compression, ti->fourcc, 4); - if ((mpeg_video != MPEG_VIDEO_V4_LAYER_2) || - !hack_engaged(ENGAGE_NATIVE_MPEG4)) - set_codec_private(ti->private_data, ti->private_size); } void video_packetizer_c::set_headers() { - // Set MinCache to 1 for I- and P-frames. If you only - // have I-frames then it can be set to 0 (e.g. MJPEG). 2 is needed - // if there are B-frames as well. - if (bframes) - set_track_min_cache(2); - else - set_track_min_cache(1); if (fps > 0.0) set_track_default_duration((int64_t)(1000000000.0 / fps)); @@ -134,87 +94,6 @@ video_packetizer_c::process(memory_c &mem, int64_t bref, int64_t fref) { int64_t timecode; - vector frames; - uint32_t i; - - debug_enter("video_packetizer_c::process"); - - if (!aspect_ratio_extracted) { - if (mpeg_video == MPEG_VIDEO_V4_LAYER_2) - extract_mpeg4_aspect_ratio(mem.data, mem.size); - else if (mpeg_video == MPEG_VIDEO_V4_LAYER_10) - extract_mpeg4_aspect_ratio(ti->private_data, ti->private_size); - aspect_ratio_extracted = true; - } - - if ((fps < 0.0) && - ((mpeg_video == MPEG_VIDEO_V1) || (mpeg_video == MPEG_VIDEO_V2))) - extract_mpeg1_2_fps(mem.data, mem.size); - - if ((mpeg_video == MPEG_VIDEO_V4_LAYER_2) && - hack_engaged(ENGAGE_NATIVE_MPEG4)) - mpeg4_find_frame_types(mem.data, mem.size, frames); - - if ((mpeg_video == MPEG_VIDEO_V4_LAYER_2) && - hack_engaged(ENGAGE_NATIVE_MPEG4) && (fps != 0.0)) { - for (i = 0; i < frames.size(); i++) { - if ((frames[i].type == 'I') || - ((frames[i].type != 'B') && (fref_frame.type != '?'))) - flush_frames(frames[i].type); - - if (old_timecode == -1) - timecode = (int64_t)(1000000000.0 * frames_output / fps) + - duration_shift; - else - timecode = old_timecode; - - if ((duration == -1) || (duration == (int64_t)(1000.0 / fps))) - duration = (int64_t)(1000000000.0 / fps); - else - duration_shift += duration - (int64_t)(1000000000.0 / fps); - - frames_output++; - frames[i].timecode = timecode; - frames[i].duration = duration; - frames[i].data = (unsigned char *)safememdup(&mem.data[frames[i].pos], - frames[i].size); - - if (frames[i].type == 'I') { - frames[i].bref = -1; - frames[i].fref = -1; - if (bref_frame.type == '?') { - bref_frame = frames[i]; - memory_c mem(frames[i].data, frames[i].size, false); - add_packet(mem, frames[i].timecode, frames[i].duration); - } else - fref_frame = frames[i]; - - } else if (frames[i].type != 'B') { - frames_output--; - if (bref_frame.type == '?') - mxerror("video_packetizer: Found a P frame but no I frame. This " - "should not have happened. Either this is a bug in mkvmerge " - "or the video stream is damaged.\n"); - frames[i].bref = bref_frame.timecode; - frames[i].fref = -1; - fref_frame = frames[i]; - - } else { - if (!bframes) { - set_codec_id(MKV_V_MPEG4_ASP); - set_track_min_cache(2); - rerender_track_headers(); - } - bframes = true; - queued_frames.push_back(frames[i]); - - } - } - - debug_leave("video_packetizer_c::process"); - - return FILE_STATUS_MOREDATA; - } if (old_timecode == -1) timecode = (int64_t)(1000000000.0 * frames_output / fps) + duration_shift; @@ -240,14 +119,9 @@ video_packetizer_c::process(memory_c &mem, ref_timecode = timecode; } - debug_leave("video_packetizer_c::process"); - return FILE_STATUS_MOREDATA; } -video_packetizer_c::~video_packetizer_c() { -} - void video_packetizer_c::dump_debug_info() { mxdebug("video_packetizer_c: queue: %d; frames_output: %d; " @@ -255,39 +129,125 @@ video_packetizer_c::dump_debug_info() { ref_timecode); } -void -video_packetizer_c::flush() { - flush_frames(true); +connection_result_e +video_packetizer_c::can_connect_to(generic_packetizer_c *src) { + video_packetizer_c *vsrc; + + vsrc = dynamic_cast(src); + if (vsrc == NULL) + return CAN_CONNECT_NO_FORMAT; + if ((width != vsrc->width) || (height != vsrc->height) || + (fps != vsrc->fps) || (hcodec_id != vsrc->hcodec_id)) + return CAN_CONNECT_NO_PARAMETERS; + if (((ti->private_data == NULL) && (vsrc->ti->private_data != NULL)) || + ((ti->private_data != NULL) && (vsrc->ti->private_data == NULL)) || + (ti->private_size != vsrc->ti->private_size)) + return CAN_CONNECT_NO_PARAMETERS; + if ((ti->private_data != NULL) && + memcmp(ti->private_data, vsrc->ti->private_data, ti->private_size)) + return CAN_CONNECT_NO_PARAMETERS; + return CAN_CONNECT_YES; } -void -video_packetizer_c::extract_mpeg4_aspect_ratio(const unsigned char *buffer, - int size) { - uint32_t num, den; +// ---------------------------------------------------------------- - aspect_ratio_extracted = true; - if (ti->aspect_ratio_given || ti->display_dimensions_given) - return; +mpeg1_2_video_packetizer_c:: +mpeg1_2_video_packetizer_c(generic_reader_c *_reader, + int _version, + double _fps, + int _width, + int _height, + int _dwidth, + int _dheight, + bool _framed, + track_info_c *_ti): + video_packetizer_c(_reader, "V_MPEG1", _fps, _width, _height, _ti), + framed(_framed), aspect_ratio_extracted(false) { - if (((mpeg_video == MPEG_VIDEO_V4_LAYER_2) && - mpeg4_extract_par(buffer, size, num, den)) - || - ((mpeg_video == MPEG_VIDEO_V4_LAYER_10) && - mpeg4_l10_extract_par(buffer, size, num, den))) { - ti->aspect_ratio_given = true; - ti->aspect_ratio = (float)hvideo_pixel_width / - (float)hvideo_pixel_height * (float)num / (float)den; - generic_packetizer_c::set_headers(); - rerender_track_headers(); - mxinfo("Track %lld of '%s': Extracted the aspect ratio information " - "from the MPEG4 video data and set the display dimensions to " - "%u/%u.\n", (int64_t)ti->id, ti->fname.c_str(), - (uint32_t)ti->display_width, (uint32_t)ti->display_height); + set_codec_id(mxsprintf("V_MPEG%d", _version)); + if (!ti->aspect_ratio_given && !ti->display_dimensions_given) { + if ((_dwidth > 0) && (_dheight > 0)) { + ti->display_dimensions_given = true; + ti->display_width = _dwidth; + ti->display_height = _dheight; + } + } else + aspect_ratio_extracted = true; +} + +int +mpeg1_2_video_packetizer_c::process(memory_c &mem, + int64_t timecode, + int64_t duration, + int64_t bref, + int64_t fref) { + unsigned char *data_ptr; + int new_bytes, state; + + if (fps < 0.0) + extract_fps(mem.data, mem.size); + + if (!aspect_ratio_extracted) + extract_aspect_ratio(mem.data, mem.size); + + if (framed) { + assert(0); } + + state = parser.GetState(); + if ((state == MPV_PARSER_STATE_EOS) || + (state == MPV_PARSER_STATE_ERROR)) + return FILE_STATUS_DONE; + + data_ptr = mem.data; + new_bytes = mem.size; + + do { + int bytes_to_add; + + bytes_to_add = (parser.GetFreeBufferSpace() < new_bytes) ? + parser.GetFreeBufferSpace() : new_bytes; + if (bytes_to_add > 0) { + parser.WriteData(data_ptr, bytes_to_add); + data_ptr += bytes_to_add; + new_bytes -= bytes_to_add; + } + + state = parser.GetState(); + while (state == MPV_PARSER_STATE_FRAME) { + MPEGFrame *frame; + + frame = parser.ReadFrame(); + if (frame == NULL) + break; + + if (hcodec_private == NULL) + create_private_data(); + + memory_c new_mem(frame->data, frame->size, true); + video_packetizer_c::process(new_mem, frame->timecode, frame->duration, + frame->firstRef, frame->secondRef); + frame->data = NULL; + delete frame; + + state = parser.GetState(); + } + } while (new_bytes > 0); + + return FILE_STATUS_MOREDATA; } void -video_packetizer_c::extract_mpeg1_2_fps(const unsigned char *buffer, +mpeg1_2_video_packetizer_c::flush() { + memory_c dummy((unsigned char *)"", 0, false); + + parser.SetEOS(); + process(dummy); + video_packetizer_c::flush(); +} + +void +mpeg1_2_video_packetizer_c::extract_fps(const unsigned char *buffer, int size) { int idx; @@ -303,8 +263,128 @@ video_packetizer_c::extract_mpeg1_2_fps(const unsigned char *buffer, } void -video_packetizer_c::flush_frames(char next_frame, - bool flush_all) { +mpeg1_2_video_packetizer_c::extract_aspect_ratio(const unsigned char *buffer, + int size) { + float ar; + + if (ti->aspect_ratio_given || ti->display_dimensions_given) + return; + + if (mpeg1_2_extract_ar(buffer, size, ar)) { + ti->display_dimensions_given = true; + if ((ar <= 0) || (ar == 1)) + set_video_display_width(width); + else + set_video_display_width((int)(height * ar)); + set_video_display_height(height); + rerender_track_headers(); + aspect_ratio_extracted = true; + } +} + +void +mpeg1_2_video_packetizer_c::create_private_data() { + MPEGChunk *raw_seq_hdr; + + raw_seq_hdr = parser.GetRealSequenceHeader(); + if (raw_seq_hdr != NULL) { + set_codec_private(raw_seq_hdr->GetPointer(), raw_seq_hdr->GetSize()); + rerender_track_headers(); + } +} + +// ---------------------------------------------------------------- + +mpeg4_l2_video_packetizer_c:: +mpeg4_l2_video_packetizer_c(generic_reader_c *_reader, + double _fps, + int _width, + int _height, + bool _input_is_native, + track_info_c *_ti): + video_packetizer_c(_reader, MKV_V_MPEG4_ASP, _fps, _width, _height, _ti), + aspect_ratio_extracted(false), input_is_native(_input_is_native) { + + assert(!input_is_native); + + if (!hack_engaged(ENGAGE_NATIVE_MPEG4) || (_fps == 0.0)) { + set_codec_id(MKV_V_MSCOMP); + check_fourcc(); + } +} + +int +mpeg4_l2_video_packetizer_c::process(memory_c &mem, + int64_t old_timecode, + int64_t duration, + int64_t bref, + int64_t fref) { + int64_t timecode; + vector frames; + int i; + + if (!aspect_ratio_extracted) + extract_aspect_ratio(mem.data, mem.size); + + if (!hack_engaged(ENGAGE_NATIVE_MPEG4) || (fps == 0.0)) { + video_packetizer_c::process(mem, old_timecode, duration, bref, fref); + return FILE_STATUS_MOREDATA; + } + + mpeg4_find_frame_types(mem.data, mem.size, frames); + + for (i = 0; i < frames.size(); i++) { + if ((frames[i].type == 'I') || + ((frames[i].type != 'B') && (fref_frame.type != '?'))) + flush_frames(frames[i].type); + + if (old_timecode == -1) + timecode = (int64_t)(1000000000.0 * frames_output / fps) + + duration_shift; + else + timecode = old_timecode; + + if ((duration == -1) || (duration == (int64_t)(1000.0 / fps))) + duration = (int64_t)(1000000000.0 / fps); + else + duration_shift += duration - (int64_t)(1000000000.0 / fps); + + frames_output++; + frames[i].timecode = timecode; + frames[i].duration = duration; + frames[i].data = (unsigned char *)safememdup(&mem.data[frames[i].pos], + frames[i].size); + + if (frames[i].type == 'I') { + frames[i].bref = -1; + frames[i].fref = -1; + if (bref_frame.type == '?') { + bref_frame = frames[i]; + memory_c mem(frames[i].data, frames[i].size, false); + add_packet(mem, frames[i].timecode, frames[i].duration); + } else + fref_frame = frames[i]; + + } else if (frames[i].type != 'B') { + frames_output--; + if (bref_frame.type == '?') + mxerror("video_packetizer: Found a P frame but no I frame. This " + "should not have happened. Either this is a bug in mkvmerge " + "or the video stream is damaged.\n"); + frames[i].bref = bref_frame.timecode; + frames[i].fref = -1; + fref_frame = frames[i]; + + } else + queued_frames.push_back(frames[i]); + } + + return FILE_STATUS_MOREDATA; +} + +void +mpeg4_l2_video_packetizer_c::flush_frames(char next_frame, + bool flush_all) { uint32_t i; if (bref_frame.type == '?') { @@ -363,102 +443,61 @@ video_packetizer_c::flush_frames(char next_frame, bref_frame.type = '?'; } -connection_result_e -video_packetizer_c::can_connect_to(generic_packetizer_c *src) { - video_packetizer_c *vsrc; +void +mpeg4_l2_video_packetizer_c::flush() { + flush_frames(true); +} - vsrc = dynamic_cast(src); - if (vsrc == NULL) - return CAN_CONNECT_NO_FORMAT; - if ((width != vsrc->width) || (height != vsrc->height) || - (fps != vsrc->fps) || (hcodec_id != vsrc->hcodec_id)) - return CAN_CONNECT_NO_PARAMETERS; - if (((ti->private_data == NULL) && (vsrc->ti->private_data != NULL)) || - ((ti->private_data != NULL) && (vsrc->ti->private_data == NULL)) || - (ti->private_size != vsrc->ti->private_size)) - return CAN_CONNECT_NO_PARAMETERS; - if ((ti->private_data != NULL) && - memcmp(ti->private_data, vsrc->ti->private_data, ti->private_size)) - return CAN_CONNECT_NO_PARAMETERS; - return CAN_CONNECT_YES; +void +mpeg4_l2_video_packetizer_c::extract_aspect_ratio(const unsigned char *buffer, + int size) { + uint32_t num, den; + + aspect_ratio_extracted = true; + if (ti->aspect_ratio_given || ti->display_dimensions_given) + return; + + if (mpeg4_extract_par(buffer, size, num, den)) { + ti->aspect_ratio_given = true; + ti->aspect_ratio = (float)hvideo_pixel_width / + (float)hvideo_pixel_height * (float)num / (float)den; + generic_packetizer_c::set_headers(); + rerender_track_headers(); + mxinfo("Track %lld of '%s': Extracted the aspect ratio information " + "from the MPEG4 layer 2 video data and set the display dimensions " + "to %u/%u.\n", (int64_t)ti->id, ti->fname.c_str(), + (uint32_t)ti->display_width, (uint32_t)ti->display_height); + } } // ---------------------------------------------------------------- -mpeg_12_video_packetizer_c:: -mpeg_12_video_packetizer_c(generic_reader_c *_reader, - int _version, - double _fps, - int _width, - int _height, - int _dwidth, - int _dheight, - track_info_c *_ti): - video_packetizer_c(_reader, "V_MPEG1", _fps, _width, _height, true, _ti) { +mpeg4_l10_video_packetizer_c:: +mpeg4_l10_video_packetizer_c(generic_reader_c *_reader, + double _fps, + int _width, + int _height, + track_info_c *_ti): + video_packetizer_c(_reader, MKV_V_MPEG4_AVC, _fps, _width, _height, _ti) { - mpeg_video = (_version == 1) ? MPEG_VIDEO_V1 : MPEG_VIDEO_V2; - set_codec_id(mxsprintf("V_MPEG%d", _version)); - if (!ti->aspect_ratio_given && !ti->display_dimensions_given) { - ti->display_dimensions_given = true; - ti->display_width = _dwidth; - ti->display_height = _dheight; - } -} - -int -mpeg_12_video_packetizer_c::process(memory_c &mem, - int64_t, - int64_t, - int64_t, - int64_t) { - unsigned char *data_ptr; - int new_bytes, state; - - state = parser.GetState(); - if ((state == MPV_PARSER_STATE_EOS) || - (state == MPV_PARSER_STATE_ERROR)) - return FILE_STATUS_DONE; - - data_ptr = mem.data; - new_bytes = mem.size; - - do { - int bytes_to_add; - - bytes_to_add = (parser.GetFreeBufferSpace() < new_bytes) ? - parser.GetFreeBufferSpace() : new_bytes; - if (bytes_to_add > 0) { - parser.WriteData(data_ptr, bytes_to_add); - data_ptr += bytes_to_add; - new_bytes -= bytes_to_add; - } - - state = parser.GetState(); - while (state == MPV_PARSER_STATE_FRAME) { - MPEGFrame *frame; - - frame = parser.ReadFrame(); - if (frame == NULL) - break; - - memory_c new_mem(frame->data, frame->size, true); - video_packetizer_c::process(new_mem, frame->timecode, frame->duration, - frame->firstRef, frame->secondRef); - frame->data = NULL; - delete frame; - - state = parser.GetState(); - } - } while (new_bytes > 0); - - return FILE_STATUS_MOREDATA; + if ((ti->private_data != NULL) && (ti->private_size > 0)) + extract_aspect_ratio(); } void -mpeg_12_video_packetizer_c::flush() { - memory_c dummy((unsigned char *)"", 0, false); +mpeg4_l10_video_packetizer_c::extract_aspect_ratio() { + uint32_t num, den; - parser.SetEOS(); - process(dummy); - video_packetizer_c::flush(); + if (ti->aspect_ratio_given || ti->display_dimensions_given) + return; + + if (mpeg4_l10_extract_par(ti->private_data, ti->private_size, num, den)) { + ti->aspect_ratio_given = true; + ti->aspect_ratio = (float)hvideo_pixel_width / + (float)hvideo_pixel_height * (float)num / (float)den; + mxinfo("Track %lld of '%s': Extracted the aspect ratio information " + "from the MPEG-4 layer 10 (AVC) video data and set the display " + "dimensions to %u/%u.\n", (int64_t)ti->id, ti->fname.c_str(), + (uint32_t)ti->display_width, (uint32_t)ti->display_height); + } } diff --git a/src/output/p_video.h b/src/output/p_video.h index 1924a58d9..3cf3a3ee2 100644 --- a/src/output/p_video.h +++ b/src/output/p_video.h @@ -30,26 +30,19 @@ class video_packetizer_c: public generic_packetizer_c { protected: double fps; - int width, height, bpp, frames_output; + int width, height, frames_output; int64_t ref_timecode, duration_shift; - bool avi_compat_mode, bframes, pass_through; - bool aspect_ratio_extracted; - mpeg_video_type_e mpeg_video; - vector queued_frames; - video_frame_t bref_frame, fref_frame; + bool pass_through; public: - video_packetizer_c(generic_reader_c *nreader, const char *ncodec_id, - double nfps, int nwidth, int nheight, bool nbframes, - track_info_c *nti) - throw (error_c); - virtual ~video_packetizer_c(); + video_packetizer_c(generic_reader_c *_reader, const char *_codec_id, + double _fps, int _width, int _height, + track_info_c *_ti); virtual int process(memory_c &mem, int64_t old_timecode = -1, int64_t duration = -1, int64_t bref = VFT_IFRAME, int64_t fref = VFT_NOBFRAME); virtual void set_headers(); - virtual void flush(); virtual void dump_debug_info(); @@ -59,25 +52,60 @@ public: virtual connection_result_e can_connect_to(generic_packetizer_c *src); protected: - virtual void flush_frames(char next_frame = '?', bool flush_all = false); - virtual void extract_mpeg4_aspect_ratio(const unsigned char *buffer, - int size); - virtual void extract_mpeg1_2_fps(const unsigned char *buffer, int size); + virtual void check_fourcc(); }; -class mpeg_12_video_packetizer_c: public video_packetizer_c { +class mpeg1_2_video_packetizer_c: public video_packetizer_c { protected: M2VParser parser; + bool framed, aspect_ratio_extracted; public: - mpeg_12_video_packetizer_c(generic_reader_c *_reader, int _version, + mpeg1_2_video_packetizer_c(generic_reader_c *_reader, int _version, double _fps, int _width, int _height, - int _dwidth, int _dheight, track_info_c *_ti); + int _dwidth, int _dheight, bool _framed, + track_info_c *_ti); virtual int process(memory_c &mem, int64_t old_timecode = -1, int64_t duration = -1, int64_t bref = VFT_IFRAME, int64_t fref = VFT_NOBFRAME); virtual void flush(); + +protected: + virtual void extract_fps(const unsigned char *buffer, int size); + virtual void extract_aspect_ratio(const unsigned char *buffer, int size); + virtual void create_private_data(); +}; + +class mpeg4_l2_video_packetizer_c: public video_packetizer_c { +protected: + vector queued_frames; + video_frame_t bref_frame, fref_frame; + bool aspect_ratio_extracted, input_is_native; + +public: + mpeg4_l2_video_packetizer_c(generic_reader_c *_reader, + double _fps, int _width, int _height, + bool _input_is_native, track_info_c *_ti); + + virtual int process(memory_c &mem, int64_t old_timecode = -1, + int64_t duration = -1, int64_t bref = VFT_IFRAME, + int64_t fref = VFT_NOBFRAME); + virtual void flush(); + +protected: + virtual void flush_frames(char next_frame = '?', bool flush_all = false); + virtual void extract_aspect_ratio(const unsigned char *buffer, int size); +}; + +class mpeg4_l10_video_packetizer_c: public video_packetizer_c { +public: + mpeg4_l10_video_packetizer_c(generic_reader_c *_reader, + double _fps, int _width, int _height, + track_info_c *_ti); + +protected: + virtual void extract_aspect_ratio(); }; #endif // __P_VIDEO_H