1 /** MPEG video helper functions (MPEG 1, 2 and 4)
2
3 mkvmerge -- utility for splicing together matroska files
4 from component media subtypes
5
6 Distributed under the GPL v2
7 see the file COPYING for details
8 or visit https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
9
10 \file
11
12 \author Written by Moritz Bunkus <moritz@bunkus.org>.
13 */
14
15 #include "common/common_pch.h"
16
17 #include "common/debugging.h"
18 #include "common/endian.h"
19 #include "common/mpeg1_2.h"
20
21 namespace mtx::mpeg1_2 {
22
23 namespace {
24 debugging_option_c s_debug{"mpeg1_2"};
25 }
26
27 /** \brief Extract the FPS from a MPEG video sequence header
28
29 This function looks for a MPEG sequence header in a buffer containing
30 a MPEG1 or MPEG2 video frame. If such a header is found its
31 FPS index is extracted and returned. This index can be mapped to the
32 actual number of frames per second with the function
33 ::mpeg_video_get_fps
34
35 \param buffer The buffer to search for the header.
36 \param buffer_size The buffer size.
37
38 \return The index or \c -1 if no MPEG sequence header was found or
39 if the buffer was too small.
40 */
41 int
extract_fps_idx(unsigned char const * buffer,int buffer_size)42 extract_fps_idx(unsigned char const *buffer,
43 int buffer_size) {
44 mxdebug_if(s_debug, fmt::format("mpeg_video_fps: start search in {0} bytes\n", buffer_size));
45 if (buffer_size < 8) {
46 mxdebug_if(s_debug, "mpeg_video_fps: sequence header too small\n");
47 return -1;
48 }
49 auto marker = get_uint32_be(buffer);
50 int idx = 4;
51 while ((idx < buffer_size) && (marker != SEQUENCE_HEADER_START_CODE)) {
52 marker <<= 8;
53 marker |= buffer[idx];
54 idx++;
55 }
56
57 if ((idx + 3) >= buffer_size) {
58 mxdebug_if(s_debug, "mpeg_video_fps: no full sequence header start code found\n");
59 return -1;
60 }
61
62 mxdebug_if(s_debug, fmt::format("mpeg_video_fps: found sequence header start code at {0}\n", idx - 4));
63
64 return buffer[idx + 3] & 0x0f;
65 }
66
67 /** \brief Extract the aspect ratio from a MPEG video sequence header
68
69 This function looks for a MPEG sequence header in a buffer containing
70 a MPEG1 or MPEG2 video frame. If such a header is found its
71 aspect ratio is extracted and returned.
72
73 \param buffer The buffer to search for the header.
74 \param buffer_size The buffer size.
75
76 \return \c true if a MPEG sequence header was found and \c false otherwise.
77 */
78 std::optional<mtx_mp_rational_t>
extract_aspect_ratio(unsigned char const * buffer,int buffer_size)79 extract_aspect_ratio(unsigned char const *buffer,
80 int buffer_size) {
81 uint32_t marker;
82 int idx;
83
84 mxdebug_if(s_debug, fmt::format("mpeg_video_ar: start search in {0} bytes\n", buffer_size));
85 if (buffer_size < 8) {
86 mxdebug_if(s_debug, "mpeg_video_ar: sequence header too small\n");
87 return {};
88 }
89 marker = get_uint32_be(buffer);
90 idx = 4;
91 while ((idx < buffer_size) && (marker != SEQUENCE_HEADER_START_CODE)) {
92 marker <<= 8;
93 marker |= buffer[idx];
94 idx++;
95 }
96 if (idx >= buffer_size) {
97 mxdebug_if(s_debug, "mpeg_video_ar: no sequence header start code found\n");
98 return {};
99 }
100
101 mxdebug_if(s_debug, fmt::format("mpeg_video_ar: found sequence header start code at {0}\n", idx - 4));
102 idx += 3; // width and height
103 if (idx >= buffer_size) {
104 mxdebug_if(s_debug, "mpeg_video_ar: sequence header too small\n");
105 return {};
106 }
107
108 switch (buffer[idx] & 0xf0) {
109 case AR_1_1: return mtx::rational(1, 1);
110 case AR_4_3: return mtx::rational(4, 3);
111 case AR_16_9: return mtx::rational(16, 9);
112 case AR_2_21: return mtx::rational(221, 100);
113 }
114
115 return {};
116 }
117
118 /** \brief Get the number of frames per second
119
120 Converts the index returned by ::mpeg_video_extract_fps_idx to a number.
121
122 \param idx The index as to convert.
123
124 \return The number of frames per second or \c -1.0 if the index was
125 invalid.
126 */
127 double
get_fps(int idx)128 get_fps(int idx) {
129 static const int s_fps[8] = {0, 24, 25, 0, 30, 50, 0, 60};
130
131 return ((idx < 1) || (idx > 8)) ? -1.0
132 : FPS_23_976 == idx ? 24000.0 / 1001.0
133 : FPS_29_97 == idx ? 30000.0 / 1001.0
134 : FPS_59_94 == idx ? 60000.0 / 1001.0
135 : s_fps[idx - 1];
136 }
137
138 bool
is_fourcc(uint32_t fourcc)139 is_fourcc(uint32_t fourcc) {
140 return (FOURCC_MPEG1 == fourcc) || (FOURCC_MPEG2 == fourcc);
141 }
142
143 } // namespace mtx::mpeg1_2
144