mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-24 20:01:53 +00:00
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:
parent
212464d6f9
commit
3d92dc63f3
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user