1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Implemented parsing frame rate and image size for MPEG2, H.264 and H.265

This commit is contained in:
Klaus Schmidinger 2022-11-22 11:31:39 +01:00
parent d756628297
commit 93d578d9b8
3 changed files with 312 additions and 6 deletions

View File

@ -2457,6 +2457,7 @@ Christoph Haubrich <christoph1.haubrich@arcor.de>
for reporting missing '0x09=H.265 video, 0x19 = AC4 audio' in vdr.5 for reporting missing '0x09=H.265 video, 0x19 = AC4 audio' in vdr.5
for reporting a problem with the call to EpgHandlers.EndSegmentTransfer() for reporting a problem with the call to EpgHandlers.EndSegmentTransfer()
for fixing handling zero bytes in cH264Parser for fixing handling zero bytes in cH264Parser
for implementing parsing frame rate and image size for MPEG2, H.264 and H.265
Pekka Mauno <pekka.mauno@iki.fi> Pekka Mauno <pekka.mauno@iki.fi>
for fixing cSchedule::GetFollowingEvent() in case there is currently no present for fixing cSchedule::GetFollowingEvent() in case there is currently no present

View File

@ -9780,7 +9780,7 @@ Video Disk Recorder Revision History
out by Onur Sentürk). out by Onur Sentürk).
- Official release. - Official release.
2022-11-20: 2022-11-22:
- Added UPDATE-2.6.0, which was missing in the official 2.6.0 release. - Added UPDATE-2.6.0, which was missing in the official 2.6.0 release.
- Fixed unexpected calls of the '-r' script when a recording is interrupted and - Fixed unexpected calls of the '-r' script when a recording is interrupted and
@ -9801,3 +9801,5 @@ Video Disk Recorder Revision History
at the same time. at the same time.
- Added a missing 'const' to cTimers::GetTimerForEvent() (reported by Markus Ehrnsperger). - Added a missing 'const' to cTimers::GetTimerForEvent() (reported by Markus Ehrnsperger).
- Added a chapter about locking to PLUGINS.html (suggested by Markus Ehrnsperger). - Added a chapter about locking to PLUGINS.html (suggested by Markus Ehrnsperger).
- Implemented parsing frame rate and image size for MPEG2, H.264 and H.265 (thanks
to Christoph Haubrich).

313
remux.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: remux.c 5.2 2022/01/18 14:24:33 kls Exp $ * $Id: remux.c 5.3 2022/11/22 11:31:39 kls Exp $
*/ */
#include "remux.h" #include "remux.h"
@ -1174,6 +1174,10 @@ protected:
bool newFrame; bool newFrame;
bool independentFrame; bool independentFrame;
int iFrameTemporalReferenceOffset; int iFrameTemporalReferenceOffset;
uint16_t frameWidth;
uint16_t frameHeight;
double framesPerSecond;
bool progressive;
public: public:
cFrameParser(void); cFrameParser(void);
virtual ~cFrameParser() {}; virtual ~cFrameParser() {};
@ -1188,6 +1192,10 @@ public:
bool NewFrame(void) { return newFrame; } bool NewFrame(void) { return newFrame; }
bool IndependentFrame(void) { return independentFrame; } bool IndependentFrame(void) { return independentFrame; }
int IFrameTemporalReferenceOffset(void) { return iFrameTemporalReferenceOffset; } int IFrameTemporalReferenceOffset(void) { return iFrameTemporalReferenceOffset; }
uint16_t FrameWidth(void) { return frameWidth; }
uint16_t FrameHeight(void) { return frameHeight; }
double FramesPerSecond(void) { return framesPerSecond; }
bool Progressive(void) { return progressive; }
}; };
cFrameParser::cFrameParser(void) cFrameParser::cFrameParser(void)
@ -1196,6 +1204,10 @@ cFrameParser::cFrameParser(void)
newFrame = false; newFrame = false;
independentFrame = false; independentFrame = false;
iFrameTemporalReferenceOffset = 0; iFrameTemporalReferenceOffset = 0;
frameWidth = 0;
frameHeight = 0;
framesPerSecond = 0.0;
progressive = false;
} }
// --- cAudioParser ---------------------------------------------------------- // --- cAudioParser ----------------------------------------------------------
@ -1229,6 +1241,18 @@ private:
uint32_t scanner; uint32_t scanner;
bool seenIndependentFrame; bool seenIndependentFrame;
int lastIFrameTemporalReference; int lastIFrameTemporalReference;
bool seenScanType;
const double frame_rate_table[9] = {
0, // 0 forbidden
24000./1001., // 1 23.976...
24., // 2 24
25., // 3 25
30000./1001., // 4 29.97...
30., // 5 30
50., // 6 50
60000./1001., // 7 59.94...
60. // 8 60
};
public: public:
cMpeg2Parser(void); cMpeg2Parser(void);
virtual int Parse(const uchar *Data, int Length, int Pid); virtual int Parse(const uchar *Data, int Length, int Pid);
@ -1239,6 +1263,7 @@ cMpeg2Parser::cMpeg2Parser(void)
scanner = EMPTY_SCANNER; scanner = EMPTY_SCANNER;
seenIndependentFrame = false; seenIndependentFrame = false;
lastIFrameTemporalReference = -1; // invalid lastIFrameTemporalReference = -1; // invalid
seenScanType = false;
} }
int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid) int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid)
@ -1292,6 +1317,27 @@ int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid)
tsPayload.Statistics(); tsPayload.Statistics();
break; break;
} }
else if (frameWidth == 0 && scanner == 0x000001B3) { // Sequence header code
frameWidth = tsPayload.GetByte() << 4;
uchar b = tsPayload.GetByte(); // ignoring two MSB of width and height in sequence extension
frameWidth |= b >> 4; // as 12 Bit = max 4095 should be sufficient for all available MPEG2 streams
frameHeight = (b & 0x0F) << 8 | tsPayload.GetByte();
b = tsPayload.GetByte();
uchar frame_rate_value = b & 0x0F;
if (frame_rate_value > 0 && frame_rate_value <= 8)
framesPerSecond = frame_rate_table[frame_rate_value];
}
else if (!seenScanType && scanner == 0x000001B5) { // Extension start code
if ((tsPayload.GetByte() & 0xF0) == 0x10) { // Sequence Extension
progressive = (tsPayload.GetByte() & 0x40) != 0;
seenScanType = true;
if (debug) {
cString s = cString::sprintf("MPEG2: %d x %d%c %.2f fps", frameWidth, frameHeight, progressive ? 'p' : 'i', framesPerSecond);
dsyslog(s);
dbgframes("\n%s", *s);
}
}
}
if (tsPayload.AtPayloadStart() // stop at a new payload start to have the buffer refilled if necessary if (tsPayload.AtPayloadStart() // stop at a new payload start to have the buffer refilled if necessary
|| tsPayload.Eof()) // or if we're out of data || tsPayload.Eof()) // or if we're out of data
break; break;
@ -1461,15 +1507,17 @@ void cH264Parser::ParseAccessUnitDelimiter(void)
void cH264Parser::ParseSequenceParameterSet(void) void cH264Parser::ParseSequenceParameterSet(void)
{ {
int chroma_format_idc = 0;
int bitDepth = 0;
uchar profile_idc = GetByte(); // profile_idc uchar profile_idc = GetByte(); // profile_idc
GetByte(); // constraint_set[0-5]_flags, reserved_zero_2bits GetByte(); // constraint_set[0-5]_flags, reserved_zero_2bits
GetByte(); // level_idc GetByte(); // level_idc
GetGolombUe(); // seq_parameter_set_id GetGolombUe(); // seq_parameter_set_id
if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || profile_idc == 86 || profile_idc ==118 || profile_idc == 128) { if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || profile_idc == 86 || profile_idc ==118 || profile_idc == 128) {
int chroma_format_idc = GetGolombUe(); // chroma_format_idc chroma_format_idc = GetGolombUe(); // chroma_format_idc
if (chroma_format_idc == 3) if (chroma_format_idc == 3)
separate_colour_plane_flag = GetBit(); separate_colour_plane_flag = GetBit();
GetGolombUe(); // bit_depth_luma_minus8 bitDepth = 8 + GetGolombUe(); // bit_depth_luma_minus8
GetGolombUe(); // bit_depth_chroma_minus8 GetGolombUe(); // bit_depth_chroma_minus8
GetBit(); // qpprime_y_zero_transform_bypass_flag GetBit(); // qpprime_y_zero_transform_bypass_flag
if (GetBit()) { // seq_scaling_matrix_present_flag if (GetBit()) { // seq_scaling_matrix_present_flag
@ -1501,9 +1549,68 @@ void cH264Parser::ParseSequenceParameterSet(void)
} }
GetGolombUe(); // max_num_ref_frames GetGolombUe(); // max_num_ref_frames
GetBit(); // gaps_in_frame_num_value_allowed_flag GetBit(); // gaps_in_frame_num_value_allowed_flag
GetGolombUe(); // pic_width_in_mbs_minus1 uint16_t frame_Width = 16 * (1 + GetGolombUe()); // pic_width_in_mbs_minus1
GetGolombUe(); // pic_height_in_map_units_minus1 uint16_t frame_Height = 16 * (1 + GetGolombUe()); // pic_height_in_map_units_minus1
frame_mbs_only_flag = GetBit(); // frame_mbs_only_flag frame_mbs_only_flag = GetBit(); // frame_mbs_only_flag
if (frameWidth == 0) {
progressive = frame_mbs_only_flag;
if (!frame_mbs_only_flag) {
GetBit(); // mb_adaptive_frame_field_flag
frame_Height *= 2;
}
GetBit(); // direct_8x8_inference_flag
bool frame_cropping_flag = GetBit(); // frame_cropping_flag
if (frame_cropping_flag) {
uint16_t frame_crop_left_offset = GetGolombUe(); // frame_crop_left_offset
uint16_t frame_crop_right_offset = GetGolombUe(); // frame_crop_right_offset
uint16_t frame_crop_top_offset = GetGolombUe(); // frame_crop_top_offset
uint16_t frame_crop_bottom_offset = GetGolombUe(); // frame_crop_bottom_offset
uint16_t CropUnitX = 1;
uint16_t CropUnitY = frame_mbs_only_flag ? 1 : 2;
if (!separate_colour_plane_flag && chroma_format_idc > 0) {
if (chroma_format_idc == 1) {
CropUnitX = 2;
CropUnitY *= 2;
}
else if (chroma_format_idc == 2)
CropUnitX = 2;
}
frame_Width -= CropUnitX * (frame_crop_left_offset + frame_crop_right_offset);
frame_Height -= CropUnitY * (frame_crop_top_offset + frame_crop_bottom_offset);
}
frameWidth = frame_Width;
frameHeight = frame_Height;
// VUI parameters
if (GetBit()) { // vui_parameters_present_flag
if (GetBit()) { // aspect_ratio_info_present
int aspect_ratio_idc = GetBits(8); // aspect_ratio_idc
if (aspect_ratio_idc == 255)
GetBits(32);
}
if (GetBit()) // overscan_info_present_flag
GetBit(); // overscan_approriate_flag
if (GetBit()) { // video_signal_type_present_flag
GetBits(4); // video_format, video_full_range_flag
if (GetBit()) // colour_description_present_flag
GetBits(24); // colour_primaries, transfer_characteristics, matrix_coefficients
}
if (GetBit()) { // chroma_loc_info_present_flag
GetGolombUe(); // chroma_sample_loc_type_top_field
GetGolombUe(); // chroma_sample_loc_type_bottom_field
}
if (GetBit()) { // timing_info_present_flag
uint32_t num_units_in_tick = GetBits(32); // num_units_in_tick
uint32_t time_scale = GetBits(32); // time_scale
if (num_units_in_tick > 0)
framesPerSecond = double(time_scale) / (num_units_in_tick << 1);
}
}
if (debug) {
cString s = cString::sprintf("H.264: %d x %d%c %.2f fps %d Bit", frameWidth, frameHeight, progressive ? 'p':'i', framesPerSecond, bitDepth);
dsyslog(s);
dbgframes("\n%s", *s);
}
}
if (debug) { if (debug) {
if (gotAccessUnitDelimiter && !gotSequenceParameterSet) if (gotAccessUnitDelimiter && !gotSequenceParameterSet)
dbgframes("A"); // just for completeness dbgframes("A"); // just for completeness
@ -1570,6 +1677,7 @@ private:
nutUnspecified0 = 48, nutUnspecified0 = 48,
nutUnspecified7 = 55, nutUnspecified7 = 55,
}; };
void ParseSequenceParameterSet(void);
public: public:
cH265Parser(void); cH265Parser(void);
virtual int Parse(const uchar *Data, int Length, int Pid); virtual int Parse(const uchar *Data, int Length, int Pid);
@ -1602,6 +1710,10 @@ int cH265Parser::Parse(const uchar *Data, int Length, int Pid)
} }
break; break;
} }
else if (frameWidth == 0 && NalUnitType == nutSequenceParameterSet) {
ParseSequenceParameterSet();
gotSequenceParameterSet = true;
}
} }
if (tsPayload.AtPayloadStart() // stop at a new payload start to have the buffer refilled if necessary if (tsPayload.AtPayloadStart() // stop at a new payload start to have the buffer refilled if necessary
|| tsPayload.Eof()) // or if we're out of data || tsPayload.Eof()) // or if we're out of data
@ -1610,6 +1722,197 @@ int cH265Parser::Parse(const uchar *Data, int Length, int Pid)
return tsPayload.Used(); return tsPayload.Used();
} }
void cH265Parser::ParseSequenceParameterSet(void)
{
int separate_colour_plane_flag = 0;
uint8_t sub_layer_profile_present_flag[8];
uint8_t sub_layer_level_present_flag[8];
GetBits(4); // sps_video_parameter_set_id
int sps_max_sub_layers_minus1 = GetBits(3); // sps_max_sub_layers_minus1
GetBit(); // sps_temporal_id_nesting_flag
// begin profile_tier_level(1, sps_max_sub_layers_minus1)
GetByte();
GetByte();
GetByte();
GetByte();
GetByte();
bool general_progressive_source_flag = GetBit(); // general_progressive_source_flag
progressive = general_progressive_source_flag;
GetBit(); // general_interlaced_source_flag
GetBits(6);
GetByte();
GetByte();
GetByte();
GetByte();
GetByte();
GetByte(); // general_level_idc
for (int i = 0; i < sps_max_sub_layers_minus1; i++ ) {
sub_layer_profile_present_flag[i] = GetBit(); // sub_layer_profile_present_flag[i]
sub_layer_level_present_flag[i] = GetBit(); // sub_layer_level_present_flag[i]
}
if (sps_max_sub_layers_minus1 > 0) {
for (int i = sps_max_sub_layers_minus1; i < 8; i++ )
GetBits(2); // reserved_zero_2bits[i]
}
for (int i = 0; i < sps_max_sub_layers_minus1; i++ ) {
if (sub_layer_profile_present_flag[i] )
GetBits(88);
if (sub_layer_level_present_flag[i])
GetBits(8);
}
// end profile_tier_level
GetGolombUe(); // sps_seq_parameter_set_id
int chroma_format_idc = GetGolombUe(); // chroma_format_idc
if (chroma_format_idc == 3)
separate_colour_plane_flag = GetBit(); // separate_colour_plane_flag
frameWidth = GetGolombUe(); // pic_width_in_luma_samples
frameHeight = GetGolombUe(); // pic_height_in_luma_samples
bool conformance_window_flag = GetBit(); // conformance_window_flag
if (conformance_window_flag) {
int conf_win_left_offset = GetGolombUe(); // conf_win_left_offset
int conf_win_right_offset = GetGolombUe(); // conf_win_right_offset
int conf_win_top_offset = GetGolombUe(); // conf_win_top_offset
int conf_win_bottom_offset = GetGolombUe(); // conf_win_bottom_offset
uint16_t SubWidthC = 1;
uint16_t SubHeightC = 1;
if (!separate_colour_plane_flag && chroma_format_idc > 0) {
if (chroma_format_idc == 1) {
SubWidthC = 2;
SubHeightC = 2;
}
else if (chroma_format_idc == 2)
SubWidthC = 2;
}
frameWidth -= SubWidthC * (conf_win_left_offset + conf_win_right_offset);
frameHeight -= SubHeightC * (conf_win_top_offset + conf_win_bottom_offset);
}
int bitDepth = 8 + GetGolombUe(); // bit_depth_luma_minus8
GetGolombUe(); // bit_depth_chroma_minus8
int log2_max_pic_order_cnt_lsb_minus4 = GetGolombUe(); // log2_max_pic_order_cnt_lsb_minus4
int sps_sub_layer_ordering_info_present_flag = GetBit(); // sps_sub_layer_ordering_info_present_flag
for (int i = sps_sub_layer_ordering_info_present_flag ? 0 : sps_max_sub_layers_minus1; i <= sps_max_sub_layers_minus1; ++i) {
GetGolombUe(); // sps_max_dec_pic_buffering_minus1[i]
GetGolombUe(); // sps_max_num_reorder_pics[i]
GetGolombUe(); // sps_max_latency_increase_plus1[i]
}
GetGolombUe(); // log2_min_luma_coding_block_size_minus3
GetGolombUe(); // log2_diff_max_min_luma_coding_block_size
GetGolombUe(); // log2_min_luma_transform_block_size_minus2
GetGolombUe(); // log2_diff_max_min_luma_transform_block_size
GetGolombUe(); // max_transform_hierarchy_depth_inter
GetGolombUe(); // max_transform_hierarchy_depth_intra
if (GetBit()) { // scaling_list_enabled_flag
if (GetBit()) { // sps_scaling_list_data_present_flag
// begin scaling_list_data
for (int sizeId = 0; sizeId < 4; ++sizeId) {
for (int matrixId = 0; matrixId < 6; matrixId += (sizeId == 3) ? 3 : 1) {
if (!GetBit()) // scaling_list_pred_mode_flag[sizeId][matrixId]
GetGolombUe(); // scaling_list_pred_matrix_id_delta[sizeId][matrixId]
else {
int coefNum = min(64, (1 << (4 + (sizeId << 1))));
if (sizeId > 1)
GetGolombSe(); // scaling_list_dc_coef_minus8[sizeId2][matrixId]
for (int i = 0; i < coefNum; ++i)
GetGolombSe(); // scaling_list_delta_coef
}
}
}
}
// end scaling_list_data
}
GetBits(2); // amp_enabled_flag, sample_adaptive_offset_enabled_flag
if (GetBit()) { // pcm_enabled_flag
GetBits(8); // pcm_sample_bit_depth_luma_minus1, pcm_sample_bit_depth_chroma_minus1
GetGolombUe(); // log2_min_pcm_luma_coding_block_size_minus3
GetGolombUe(); // log2_diff_max_min_pcm_luma_coding_block_size
GetBit(); // pcm_loop_filter_disabled_flag
}
uint32_t num_short_term_ref_pic_sets = GetGolombUe(); // num_short_term_ref_pic_sets
uint32_t NumDeltaPocs[num_short_term_ref_pic_sets];
for (uint32_t stRpsIdx = 0; stRpsIdx < num_short_term_ref_pic_sets; ++stRpsIdx) {
// start of st_ref_pic_set(stRpsIdx)
bool inter_ref_pic_set_prediction_flag = false;
if (stRpsIdx != 0)
inter_ref_pic_set_prediction_flag = GetBit(); // inter_ref_pic_set_prediction_flag
if (inter_ref_pic_set_prediction_flag) {
uint32_t RefRpsIdx, delta_idx_minus1 = 0;
if (stRpsIdx == num_short_term_ref_pic_sets)
delta_idx_minus1 = GetGolombUe(); // delta_idx_minus1
GetBit(); // delta_rps_sign
GetGolombUe(); // abs_delta_rps_minus1
RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1);
NumDeltaPocs[stRpsIdx] = 0;
for (uint32_t j = 0; j <= NumDeltaPocs[RefRpsIdx]; ++j) {
if (!GetBit()) { // used_by_curr_pic_flag[j]
if (GetBit()) // use_delta_flag[j]
NumDeltaPocs[stRpsIdx]++;
}
else
NumDeltaPocs[stRpsIdx]++;
}
}
else {
uint32_t num_negative_pics = GetGolombUe(); // num_negative_pics
uint32_t num_positive_pics = GetGolombUe(); // num_positive_pics
for (uint32_t j = 0; j < num_negative_pics; ++j) {
GetGolombUe(); // delta_poc_s0_minus1[i]
GetBit(); // used_by_curr_pic_s0_flag[i]
}
for (uint32_t j = 0; j < num_positive_pics; ++j) {
GetGolombUe(); // delta_poc_s1_minus1[i]
GetBit(); // delta_poc_s1_minus1[i]
}
NumDeltaPocs[stRpsIdx] = num_negative_pics + num_positive_pics;
}
// end of st_ref_pic_set(stRpsIdx)
}
if (GetBit()) { // long_term_ref_pics_present_flag
uint32_t num_long_term_ref_pics_sps = GetGolombUe(); // num_long_term_ref_pics_sps
for (uint32_t i = 0; i < num_long_term_ref_pics_sps; ++i) {
GetBits(log2_max_pic_order_cnt_lsb_minus4 + 4); // lt_ref_pic_poc_lsb_sps[i]
GetBit(); // used_by_curr_pic_lt_sps_flag[i]
}
}
GetBits(2); // sps_temporal_mvp_enabled_flag, strong_intra_smoothing_enabled_flag
if (GetBit()) { // vui_parameters_present_flag
// begin of vui_parameters()
if (GetBit()) { // aspect_ratio_info_present_flag
int aspect_ratio_idc = GetBits(8); // aspect_ratio_idc
if (aspect_ratio_idc == 255) // EXTENDED_SAR
GetBits(32); // sar_width, sar_height
}
if (GetBit()) // overscan_info_present_flag
GetBit(); // overscan_appropriate_flag
if (GetBit()) { // video_signal_type_present_flag
GetBits(4); // video_format, video_full_range_flag
if (GetBit()) // colour_description_present_flag
GetBits(24); // colour_primaries, transfer_characteristics, matrix_coeffs
}
if (GetBit()) { // chroma_loc_info_present_flag
GetGolombUe(); // chroma_sample_loc_type_top_field
GetGolombUe(); // chroma_sample_loc_type_bottom_field
}
GetBits(3); // neutral_chroma_indication_flag, field_seq_flag, frame_field_info_present_flag
if (GetBit()) { // default_display_window_flag
GetGolombUe(); // def_disp_win_left_offset
GetGolombUe(); // def_disp_win_right_offset
GetGolombUe(); // def_disp_win_top_offset
GetGolombUe(); // def_disp_win_bottom_offset
}
if (GetBit()) { // vui_timing_info_present_flag
uint32_t vui_num_units_in_tick = GetBits(32); // vui_num_units_in_tick
uint32_t vui_time_scale = GetBits(32); // vui_time_scale
if (vui_num_units_in_tick > 0)
framesPerSecond = (double)vui_time_scale / vui_num_units_in_tick;
}
}
if (debug) {
cString s = cString::sprintf("H.265: %d x %d%c %.2f fps %d Bit", frameWidth, frameHeight, progressive ? 'p':'i', framesPerSecond, bitDepth);
dsyslog(s);
dbgframes("\n%s", *s);
}
}
// --- cFrameDetector -------------------------------------------------------- // --- cFrameDetector --------------------------------------------------------
cFrameDetector::cFrameDetector(int Pid, int Type) cFrameDetector::cFrameDetector(int Pid, int Type)