Extract the FPS from MPEG-1 and -2 video if it isn't known (e.g. if it comes from MP4).

This commit is contained in:
Moritz Bunkus 2004-11-16 09:04:34 +00:00
parent 212464d6f9
commit 3d92dc63f3
5 changed files with 167 additions and 17 deletions

View File

@ -1,4 +1,4 @@
/*
/** MPEG video helper functions (MPEG 1, 2 and 4)
* mkvmerge -- utility for splicing together matroska files
* from component media subtypes
*
@ -6,11 +6,10 @@
* see the file COPYING for details
* or visit http://www.gnu.org/copyleft/gpl.html
*
* $Id$
* \file
* \version $Id$
*
* MPEG4 video helper functions
*
* Written by Moritz Bunkus <moritz@bunkus.org>.
* \author Written by Moritz Bunkus <moritz@bunkus.org>.
*/
#include "os.h"
@ -18,6 +17,20 @@
#include "common.h"
#include "mpeg4_common.h"
/** Extract the pixel aspect ratio from a MPEG4 video frame
*
* This function searches a buffer containing a MPEG4 video frame
* for the pixel aspectc ratio. If it is found then the numerator
* and the denominator are returned.
*
* \param buffer The buffer containing the MPEG4 video frame.
* \param size The size of the buffer in bytes.
* \param par_num The numerator, if found, is stored in this variable.
* \param par_den The denominator, if found, is stored in this variable.
*
* \return \c true if the pixel aspect ratio was found and \c false
* otherwise.
*/
bool
mpeg4_extract_par(const unsigned char *buffer,
int size,
@ -82,8 +95,22 @@ mpeg4_extract_par(const unsigned char *buffer,
return false;
}
/** Find frame boundaries and frame types in a packed video frame
*
* This function searches a buffer containing one or more MPEG4 video frames
* for the frame boundaries and their types. This may be the case for B frames
* if they're glued to another frame like they are in AVI files.
*
* \param buffer The buffer containing the MPEG4 video frame(s).
* \param size The size of the buffer in bytes.
* \param frames The data for each frame that is found is put into this
* variable. See ::video_frame_t
*
* \return Nothing. If no frames were found (e.g. only the dummy header for
* a dummy frame) then \a frames will contain no elements.
*/
void
mpeg4_find_frame_types(unsigned char *buf,
mpeg4_find_frame_types(const unsigned char *buf,
int size,
vector<video_frame_t> &frames) {
bit_cursor_c bits(buf, size);
@ -108,7 +135,7 @@ mpeg4_find_frame_types(unsigned char *buf,
mxverb(3, "mpeg4_frames: found start code at %d\n",
bits.get_bit_position() / 8);
bits.skip_bits(32);
if (marker == VOP_START_CODE) {
if (marker == MPEGVIDEO_OBJECT_PLAIN_START_CODE) {
if (!bits.get_bits(2, frame_type))
break;
if (!first_frame) {
@ -141,3 +168,77 @@ mpeg4_find_frame_types(unsigned char *buf,
fit++;
}
}
/** \brief Extract the FPS 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
* FPS index is extracted and returned. This index can be mapped to the
* actual number of frames per second with the function
* ::mpeg_video_get_fps
*
* \param buffer The buffer to search for the header.
* \param size The buffer size.
*
* \return The index or \c -1 if no MPEG sequence header was found or
* if the buffer was too small.
*/
int
mpeg1_2_extract_fps_idx(const unsigned char *buffer,
int size) {
uint32_t marker;
int idx;
mxverb(3, "mpeg_video_fps: start search in %d bytes\n", size);
if (size < 8) {
mxverb(3, "mpeg_video_fps: 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_fps: no sequence header start code found\n");
return -1;
}
mxverb(3, "mpeg_video_fps: found sequence header start code at %d\n",
idx - 4);
idx += 3; // width and height
if (idx >= size) {
mxverb(3, "mpeg_video_fps: sequence header too small\n");
return -1;
}
return buffer[idx] & 0x0f;
}
/** \brief Get the number of frames per second
*
* Converts the index returned by ::mpeg_video_extract_fps_idx to a number.
*
* \param idx The index as to convert.
*
* \return The number of frames per second or \c -1.0 if the index was
* invalid.
*/
double
mpeg1_2_get_fps(int idx) {
static const int fps[8] = {0, 24, 25, 0, 30, 50, 0, 60};
if ((idx < 1) || (idx > 8))
return -1.0;
switch (idx) {
case MPEGVIDEO_FPS_23_976:
return (double)24000.0 / 1001.0;
case MPEGVIDEO_FPS_29_97:
return (double)30000.0 / 1001.0;
case MPEGVIDEO_FPS_59_94:
return (double)60000.0 / 1001.0;
default:
return fps[idx - 1];
}
}

View File

@ -8,7 +8,7 @@
*
* $Id$
*
* MPEG4 video helper functions
* MPEG1, 2 and 4 video helper functions
*
* Written by Moritz Bunkus <moritz@bunkus.org>.
*/
@ -16,7 +16,27 @@
#ifndef __MPEG4_COMMON_H
#define __MPEG4_COMMON_H
#define VOP_START_CODE 0x000001b6
/** Start code for a MPEG4 (?) video object plain */
#define MPEGVIDEO_OBJECT_PLAIN_START_CODE 0x000001b6
/** Start code for a MPEG-1 and -2 sequence header */
#define MPEGVIDEO_SEQUENCE_START_CODE 0x000001b3
/** MPEG-1/-2 frame rate: 24000/1001 frames per second */
#define MPEGVIDEO_FPS_23_976 0x01
/** MPEG-1/-2 frame rate: 24 frames per second */
#define MPEGVIDEO_FPS_24 0x02
/** MPEG-1/-2 frame rate: 25 frames per second */
#define MPEGVIDEO_FPS_25 0x03
/** MPEG-1/-2 frame rate: 30000/1001 frames per second */
#define MPEGVIDEO_FPS_29_97 0x04
/** MPEG-1/-2 frame rate: 30 frames per second */
#define MPEGVIDEO_FPS_30 0x05
/** MPEG-1/-2 frame rate: 50 frames per second */
#define MPEGVIDEO_FPS_50 0x06
/** MPEG-1/-2 frame rate: 60000/1001 frames per second */
#define MPEGVIDEO_FPS_59_94 0x07
/** MPEG-1/-2 frame rate: 60 frames per second */
#define MPEGVIDEO_FPS_60 0x08
typedef struct {
unsigned char *data;
@ -28,7 +48,11 @@ typedef struct {
bool MTX_DLL_API mpeg4_extract_par(const unsigned char *buffer, int size,
uint32_t &par_num, uint32_t &par_den);
void MTX_DLL_API mpeg4_find_frame_types(unsigned char *buf, int size,
void MTX_DLL_API mpeg4_find_frame_types(const unsigned char *buf, int size,
vector<video_frame_t> &frames);
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);
#endif /* __MPEG4_COMMON_H */

View File

@ -1166,7 +1166,7 @@ qtmp4_reader_c::create_packetizer(int64_t tid) {
codec_id = mxsprintf("V_MPEG%c", dmx->fourcc[3]);
dmx->ptzr =
add_packetizer(new video_packetizer_c(this, codec_id.c_str(),
0.0, dmx->v_width,
-1.0, dmx->v_width,
dmx->v_height, false, ti));
} else {

View File

@ -69,6 +69,8 @@ video_packetizer_c::video_packetizer_c(generic_reader_c *nreader,
!strncmp(ncodec_id, MKV_V_MPEG4_SP, strlen(MKV_V_MPEG4_SP) - 2))
is_mpeg4 = true;
is_mpeg1_2 = !strcmp(ncodec_id, "V_MPEG1") || !strcmp(ncodec_id, "V_MPEG2");
if (ncodec_id != NULL)
set_codec_id(ncodec_id);
else if (is_mpeg4 && hack_engaged(ENGAGE_NATIVE_MPEG4)) {
@ -98,7 +100,7 @@ video_packetizer_c::set_headers() {
set_track_min_cache(2);
else
set_track_min_cache(1);
if (fps != 0.0)
if (fps > 0.0)
set_track_default_duration((int64_t)(1000000000.0 / fps));
set_video_pixel_width(width);
@ -129,8 +131,12 @@ video_packetizer_c::process(memory_c &mem,
debug_enter("video_packetizer_c::process");
if (is_mpeg1_2 && (fps < 0.0))
extract_mpeg1_2_fps(mem.data, mem.size);
if (hack_engaged(ENGAGE_NATIVE_MPEG4) && is_mpeg4)
mpeg4_find_frame_types(mem.data, mem.size, frames);
if (hack_engaged(ENGAGE_NATIVE_MPEG4) && is_mpeg4 && (fps != 0.0)) {
for (i = 0; i < frames.size(); i++) {
if ((frames[i].type == 'I') ||
@ -192,7 +198,7 @@ video_packetizer_c::process(memory_c &mem,
}
if (is_mpeg4 && !aspect_ratio_extracted)
extract_mpeg4_aspect_ratio(mem);
extract_mpeg4_aspect_ratio(mem.data, mem.size);
if (old_timecode == -1)
timecode = (int64_t)(1000000000.0 * frames_output / fps) + duration_shift;
@ -239,14 +245,15 @@ video_packetizer_c::flush() {
}
void
video_packetizer_c::extract_mpeg4_aspect_ratio(memory_c &mem) {
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;
if (mpeg4_extract_par(mem.data, mem.size, num, den)) {
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;
@ -259,6 +266,22 @@ video_packetizer_c::extract_mpeg4_aspect_ratio(memory_c &mem) {
}
}
void
video_packetizer_c::extract_mpeg1_2_fps(const unsigned char *buffer,
int size) {
int idx;
idx = mpeg1_2_extract_fps_idx(buffer, size);
if (idx >= 0) {
fps = mpeg1_2_get_fps(idx);
if (fps > 0) {
set_track_default_duration((int64_t)(1000000000.0 / fps));
rerender_track_headers();
} else
fps = 0.0;
}
}
void
video_packetizer_c::flush_frames(char next_frame,
bool flush_all) {

View File

@ -31,7 +31,7 @@ private:
double fps;
int width, height, bpp, frames_output;
int64_t ref_timecode, duration_shift;
bool avi_compat_mode, bframes, pass_through, is_mpeg4;
bool avi_compat_mode, bframes, pass_through, is_mpeg4, is_mpeg1_2;
bool aspect_ratio_extracted;
vector<video_frame_t> queued_frames;
video_frame_t bref_frame, fref_frame;
@ -58,7 +58,9 @@ public:
protected:
virtual void flush_frames(char next_frame = '?', bool flush_all = false);
virtual void extract_mpeg4_aspect_ratio(memory_c &mem);
virtual void extract_mpeg4_aspect_ratio(const unsigned char *buffer,
int size);
virtual void extract_mpeg1_2_fps(const unsigned char *buffer, int size);
};
#endif // __P_VIDEO_H