summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Poet <jpoet@mythtv.org>2011-02-06 19:09:19 (GMT)
committer John Poet <jpoet@mythtv.org>2011-02-12 22:10:15 (GMT)
commit4a8c889f056a9a9d38e885dc10a3dd769e8da7c7 (patch)
treeaf5a14572ebce8c5631a090c9dd16b731a81ab60
parent8e3fd5a2e9bff8e4c21a2ab235059b7746155c67 (diff)
Fix BBC frame detection.
Patch is by Paul Gardiner with some very minor tweaks by me. Paul's comments: Remove all assumptions of alignment between NALs and the packets passed into the parser. Prior to parsing the bodies of the various NALs, remove any emulation-prevention bytes. This should improve results for all H264, not just BBC HD. Update the code for reading profile>=100 versions of the SPS. The scaling-list-present flag was being read, but not the lists themselves, and since the log2_max_frame_num_minus4 field occurs after, frame numbers were not being read correctly from slices. That was probably the biggest contributor to BBC HD problems. Fixes #9410
-rw-r--r--mythtv/libs/libmythtv/mpeg/H264Parser.cpp424
-rw-r--r--mythtv/libs/libmythtv/mpeg/H264Parser.h27
2 files changed, 331 insertions, 120 deletions
diff --git a/mythtv/libs/libmythtv/mpeg/H264Parser.cpp b/mythtv/libs/libmythtv/mpeg/H264Parser.cpp
index ac805e7..e2fd720 100644
--- a/mythtv/libs/libmythtv/mpeg/H264Parser.cpp
+++ b/mythtv/libs/libmythtv/mpeg/H264Parser.cpp
@@ -1,5 +1,6 @@
// MythTV headers
#include "H264Parser.h"
+#include <iostream>
extern "C" {
// from libavcodec
@@ -92,9 +93,13 @@ static const float eps = 1E-5;
H264Parser::H264Parser(void)
{
+ rbsp_buffer_size = 188 * 2;
+ rbsp_buffer = new uint8_t[rbsp_buffer_size];
+ if (rbsp_buffer == 0)
+ rbsp_buffer_size = 0;
+
Reset();
I_is_keyframe = true;
- memset(&gb, 0, sizeof(gb));
}
void H264Parser::Reset(void)
@@ -144,9 +149,47 @@ void H264Parser::Reset(void)
AU_offset = frame_start_offset = keyframe_start_offset = 0;
on_frame = on_key_frame = false;
+
+ resetRBSP();
}
+QString H264Parser::NAL_type_str(uint8_t type)
+{
+ switch (type)
+ {
+ case UNKNOWN:
+ return "UNKNOWN";
+ case SLICE:
+ return "SLICE";
+ case SLICE_DPA:
+ return "SLICE_DPA";
+ case SLICE_DPB:
+ return "SLICE_DPB";
+ case SLICE_DPC:
+ return "SLICE_DPC";
+ case SLICE_IDR:
+ return "SLICE_IDR";
+ case SEI:
+ return "SEI";
+ case SPS:
+ return "SPS";
+ case PPS:
+ return "PPS";
+ case AU_DELIMITER:
+ return "AU_DELIMITER";
+ case END_SEQUENCE:
+ return "END_SEQUENCE";
+ case END_STREAM:
+ return "END_STREAM";
+ case FILLER_DATA:
+ return "FILLER_DATA";
+ case SPS_EXT:
+ return "SPS_EXT";
+ }
+ return "OTHER";
+}
+
bool H264Parser::new_AU(void)
{
/*
@@ -183,28 +226,28 @@ bool H264Parser::new_AU(void)
one or more of the following ways.
- frame_num differs in value. The value of frame_num used to
- test this condition is the value of frame_num that appears in
- the syntax of the slice header, regardless of whether that value
- is inferred to have been equal to 0 for subsequent use in the
- decoding process due to the presence of
- memory_management_control_operation equal to 5.
+ test this condition is the value of frame_num that appears in
+ the syntax of the slice header, regardless of whether that value
+ is inferred to have been equal to 0 for subsequent use in the
+ decoding process due to the presence of
+ memory_management_control_operation equal to 5.
Note: If the current picture is an IDR picture FrameNum and
PrevRefFrameNum are set equal to 0.
- pic_parameter_set_id differs in value.
- field_pic_flag differs in value.
- bottom_field_flag is present in both and differs in value.
- - nal_ref_idc differs in value with one of the nal_ref_idc values
- being equal to 0.
+ - nal_ref_idc differs in value with one of the nal_ref_idc
+ values being equal to 0.
- pic_order_cnt_type is equal to 0 for both and either
- pic_order_cnt_lsb differs in value, or delta_pic_order_cnt_bottom
- differs in value.
+ pic_order_cnt_lsb differs in value, or delta_pic_order_cnt_bottom
+ differs in value.
- pic_order_cnt_type is equal to 1 for both and either
- delta_pic_order_cnt[0] differs in value, or
- delta_pic_order_cnt[1] differs in value.
+ delta_pic_order_cnt[0] differs in value, or
+ delta_pic_order_cnt[1] differs in value.
- nal_unit_type differs in value with one of the nal_unit_type values
- being equal to 5.
+ being equal to 5.
- nal_unit_type is equal to 5 for both and idr_pic_id differs in
- value.
+ value.
NOTE – Some of the VCL NAL units in redundant coded pictures or some
non-VCL NAL units (e.g. an access unit delimiter NAL unit) may also
@@ -230,6 +273,9 @@ bool H264Parser::new_AU(void)
else if ((bottom_field_flag != -1 && prev_bottom_field_flag != -1) &&
bottom_field_flag != prev_bottom_field_flag)
result = true;
+ else if ((nal_ref_idc == 0 || prev_nal_ref_idc == 0) &&
+ nal_ref_idc != prev_nal_ref_idc)
+ result = true;
else if ((pic_order_cnt_type == 0 && prev_pic_order_cnt_type == 0) &&
(pic_order_cnt_lsb != prev_pic_order_cnt_lsb ||
delta_pic_order_cnt_bottom !=
@@ -253,6 +299,7 @@ bool H264Parser::new_AU(void)
prev_pic_parameter_set_id = pic_parameter_set_id;
prev_field_pic_flag = field_pic_flag;
prev_bottom_field_flag = bottom_field_flag;
+ prev_nal_ref_idc = nal_ref_idc;
prev_pic_order_cnt_lsb = pic_order_cnt_lsb;
prev_delta_pic_order_cnt_bottom = delta_pic_order_cnt_bottom;
prev_delta_pic_order_cnt[0] = delta_pic_order_cnt[0];
@@ -263,22 +310,144 @@ bool H264Parser::new_AU(void)
return result;
}
+void H264Parser::resetRBSP(void)
+{
+ rbsp_index = 0;
+ consecutive_zeros = 0;
+ have_unfinished_NAL = false;
+}
+
+bool H264Parser::fillRBSP(const uint8_t *byteP, uint32_t byte_count,
+ bool found_start_code)
+{
+ /*
+ bitstream buffer, must be FF_INPUT_BUFFER_PADDING_SIZE
+ bytes larger then the actual data
+ */
+ uint32_t required_size = rbsp_index + byte_count +
+ FF_INPUT_BUFFER_PADDING_SIZE;
+ if (rbsp_buffer_size < required_size)
+ {
+ // Round up to packet size
+ required_size = ((required_size / 188) + 1) * 188;
+
+ /* Need a bigger buffer */
+ uint8_t *new_buffer = new uint8_t[required_size];
+
+ if (new_buffer == NULL)
+ {
+ /* Allocation failed. Discard the new bytes */
+ std::cerr << "H264Parser::fillRBSP: "
+ << "FAILED to allocate RBSP buffer!\n";
+ return false;
+ }
+
+ /* Copy across bytes from old buffer */
+ memcpy(new_buffer, rbsp_buffer, rbsp_index);
+ delete [] rbsp_buffer;
+ rbsp_buffer = new_buffer;
+ rbsp_buffer_size = required_size;
+ }
+
+ /* Fill rbsp while we have data */
+ while (byte_count)
+ {
+ /* Copy the byte into the rbsp, unless it
+ * is the 0x03 in a 0x000003 */
+ if (consecutive_zeros < 2 || *byteP != 0x03)
+ rbsp_buffer[rbsp_index++] = *byteP;
+
+ if (*byteP == 0)
+ ++consecutive_zeros;
+ else
+ consecutive_zeros = 0;
+
+ ++byteP;
+ --byte_count;
+ }
+
+ /* If we've found the next start code then that, plus the first byte of
+ * the next NAL, plus the preceding zero bytes will all be in the rbsp
+ * buffer. Move rbsp_index++ back to the end of the actual rbsp data. We
+ * need to know the correct size of the rbsp to decode some NALs. */
+ if (found_start_code)
+ {
+ if (rbsp_index >= 4)
+ {
+ rbsp_index -= 4;
+ while (rbsp_index > 0 && rbsp_buffer[rbsp_index-1] == 0)
+ --rbsp_index;
+ }
+ else
+ {
+ /* This should never happen. */
+ std::cerr << "H264Parser::fillRBSP: "
+ << "Found start code, rbsp_index is "
+ << rbsp_index << " but it should be >4\n";
+ }
+ }
+
+ /* Stick some 0xff on the end for get_bits to run into */
+ memset(&rbsp_buffer[rbsp_index], 0xff, FF_INPUT_BUFFER_PADDING_SIZE);
+ return true;
+}
+
uint32_t H264Parser::addBytes(const uint8_t *bytes,
const uint32_t byte_count,
const uint64_t stream_offset)
{
- const uint8_t *byteP = bytes;
- const uint8_t *endP = bytes + byte_count;
- uint8_t first_byte;
+ const uint8_t *startP = bytes;
+ const uint8_t *endP;
+ bool found_start_code;
- state_changed = is_keyframe = false;
+ state_changed = false;
+ on_frame = false;
+ on_key_frame = false;
- while (byteP < endP)
+ while (startP < bytes + byte_count && !on_frame)
{
- byteP = ff_find_start_code(byteP, endP, &sync_accumulator);
+ endP = ff_find_start_code(startP,
+ bytes + byte_count, &sync_accumulator);
+
+ found_start_code = ((sync_accumulator & 0xffffff00) == 0x00000100);
+
+ /* Between startP and endP we potentially have some more
+ * bytes of a NAL that we've been parsing (plus some bytes of
+ * start code) */
+ if (have_unfinished_NAL)
+ {
+ if (!fillRBSP(startP, endP - startP, found_start_code))
+ {
+ resetRBSP();
+ return endP - bytes;
+ }
+ processRBSP(found_start_code); /* Call may set have_uinfinished_NAL
+ * to false */
+ }
+
+ /* Dealt with everything up to endP */
+ startP = endP;
- if ((sync_accumulator & 0xffffff00) == 0x00000100)
+ if (found_start_code)
{
+ if (have_unfinished_NAL)
+ {
+ /* We've found a new start code, without completely
+ * parsing the previous NAL. Either there's a
+ * problem with the stream or with this parser.
+ */
+ std::cerr << "H264Parser::addBytes: Found new start code, "
+ << "but previous NAL is incomplete!\n";
+ }
+
+ /* Prepare for accepting the new NAL */
+ resetRBSP();
+
+ /* If we find the start of an AU somewhere from here
+ * to the next start code, the offset to associate with
+ * it is the one passed in to this call, not any of the
+ * subsequent calls. */
+ pkt_offset = stream_offset; // + (startP - bytes);
/*
nal_unit_type specifies the type of RBSP data structure contained in
the NAL unit as specified in Table 7-1. VCL NAL units
@@ -299,92 +468,104 @@ uint32_t H264Parser::addBytes(const uint8_t *bytes,
10 End of sequence end_of_seq_rbsp( )
11 End of stream end_of_stream_rbsp( )
*/
- first_byte = *(byteP - 1);
- nal_unit_type = first_byte & 0x1f;
- nal_ref_idc = (first_byte >> 5) & 0x3;
+ nal_unit_type = sync_accumulator & 0x1f;
+ nal_ref_idc = (sync_accumulator >> 5) & 0x3;
if (nal_unit_type == SPS || nal_unit_type == PPS ||
nal_unit_type == SEI || NALisSlice(nal_unit_type))
{
- /*
- bitstream buffer, must be FF_INPUT_BUFFER_PADDING_SIZE
- bytes larger then the actual read bits
- */
- if (byteP + 1 + FF_INPUT_BUFFER_PADDING_SIZE < endP)
- {
- init_get_bits(&gb, byteP, 8 * (endP - byteP));
-
- if (nal_unit_type == SEI)
- {
- decode_SEI(&gb);
- set_AU_pending(stream_offset);
- }
- else if (nal_unit_type == SPS)
- {
- decode_SPS(&gb);
- set_AU_pending(stream_offset);
- }
- else if (nal_unit_type == PPS)
- {
- decode_PPS(&gb);
- set_AU_pending(stream_offset);
- }
- else
- {
- decode_Header(&gb);
- if (new_AU())
- set_AU_pending(stream_offset);
- }
-
- byteP += (get_bits_count(&gb) / 8);
- }
+ /* This is a NAL we need to parse. We may have the body
+ * of it in the part of the stream past to us this call,
+ * or we may get the rest in subsequent calls to addBytes.
+ * Either way, we set have_unfinished_NAL, so that we
+ * start filling the rbsp buffer */
+ have_unfinished_NAL = true;
}
- else if (!AU_pending)
- {
- if (nal_unit_type == AU_DELIMITER ||
+ else if (nal_unit_type == AU_DELIMITER ||
(nal_unit_type > SPS_EXT &&
nal_unit_type < AUXILIARY_SLICE))
- {
- AU_pending = true;
- AU_offset = stream_offset;
- }
- else if ((nal_ref_idc == 0 || prev_nal_ref_idc == 0) &&
- nal_ref_idc != prev_nal_ref_idc)
- {
- AU_pending = true;
- AU_offset = stream_offset;
- }
+ {
+ set_AU_pending();
}
+ }
+ }
- if (AU_pending && NALisSlice(nal_unit_type))
- {
- /* Once we know the slice type of a new AU, we can
- * determine if it is a keyframe or just a frame */
+ return startP - bytes;
+}
- AU_pending = false;
- state_changed = true;
- on_frame = true;
- frame_start_offset = AU_offset;
+void H264Parser::processRBSP(bool rbsp_complete)
+{
+ GetBitContext gb;
- if (is_keyframe)
- {
- on_key_frame = true;
- keyframe_start_offset = AU_offset;
- }
- else
- on_key_frame = false;
- }
- else
- on_frame = on_key_frame = false;
+ init_get_bits(&gb, rbsp_buffer, 8 * rbsp_index);
+
+ if (nal_unit_type == SEI)
+ {
+ /* SEI cannot be parsed without knowing its size. If
+ * we haven't got the whole rbsp, return and wait for
+ * the rest */
+ if (!rbsp_complete)
+ return;
- prev_nal_ref_idc = nal_ref_idc;
+ set_AU_pending();
- return byteP - bytes;
- }
+ decode_SEI(&gb);
}
+ else if (nal_unit_type == SPS)
+ {
+ /* Best wait until we have the whole thing */
+ if (!rbsp_complete)
+ return;
- return byteP - bytes;
+ set_AU_pending();
+
+ decode_SPS(&gb);
+ }
+ else if (nal_unit_type == PPS)
+ {
+ /* Best wait until we have the whole thing */
+ if (!rbsp_complete)
+ return;
+
+ set_AU_pending();
+
+ decode_PPS(&gb);
+ }
+ else
+ {
+ /* Need only parse the header. So return only
+ * if we have insufficient bytes */
+ if (!rbsp_complete && rbsp_index < MAX_SLICE_HEADER_SIZE)
+ return;
+
+ decode_Header(&gb);
+
+ if (new_AU())
+ set_AU_pending();
+ }
+
+ /* If we got this far, we managed to parse a sufficient
+ * prefix of the current NAL. We can go onto the next. */
+ have_unfinished_NAL = false;
+
+ if (AU_pending && NALisSlice(nal_unit_type))
+ {
+ /* Once we know the slice type of a new AU, we can
+ * determine if it is a keyframe or just a frame */
+
+ AU_pending = false;
+ state_changed = true;
+
+ on_frame = true;
+ frame_start_offset = AU_offset;
+
+ if (is_keyframe || au_contains_keyframe_message)
+ {
+ on_key_frame = true;
+ keyframe_start_offset = AU_offset;
+ }
+ }
}
/*
@@ -394,9 +575,11 @@ bool H264Parser::decode_Header(GetBitContext *gb)
{
uint first_mb_in_slice;
+ is_keyframe = false;
+
if (log2_max_frame_num == 0 || pic_order_present_flag == -1)
{
- // SPS or PPS has not been parsed yet
+ /* SPS or PPS has not been parsed yet */
return false;
}
@@ -600,7 +783,14 @@ void H264Parser::decode_SPS(GetBitContext * gb)
{
for (int idx = 0; idx < ((chroma_format_idc != 3) ? 8 : 12); ++idx)
{
- get_bits1(gb); // scaling_list
+ if (get_bits1(gb)) // Scaling list presnent
+ {
+ int sl_n = ((idx < 6) ? 16 : 64);
+ for(int sl_i = 0; sl_i < sl_n; sl_i++)
+ {
+ get_se_golomb(gb);
+ }
+ }
}
}
}
@@ -838,30 +1028,34 @@ void H264Parser::decode_SEI(GetBitContext *gb)
int type = 0, size = 0;
- do {
- type += show_bits(gb, 8);
- } while (get_bits(gb, 8) == 255);
-
- do {
- size += show_bits(gb, 8);
- } while (get_bits(gb, 8) == 255);
-
- switch (type)
+ /* A message requires at least 2 bytes, and then
+ * there's the stop bit plus alignment, so there
+ * can be no message in less than 24 bits */
+ while (get_bits_left(gb) >= 24)
{
- case SEI_TYPE_RECOVERY_POINT:
- recovery_frame_cnt = get_ue_golomb(gb);
- exact_match_flag = get_bits1(gb);
- broken_link_flag = get_bits1(gb);
- changing_group_slice_idc = get_bits(gb, 2);
- is_keyframe |= (recovery_frame_cnt >= 0);
- return;
+ do {
+ type += show_bits(gb, 8);
+ } while (get_bits(gb, 8) == 255);
- default:
- skip_bits(gb, size * 8);
- break;
- }
+ do {
+ size += show_bits(gb, 8);
+ } while (get_bits(gb, 8) == 255);
- align_get_bits(gb);
+ switch (type)
+ {
+ case SEI_TYPE_RECOVERY_POINT:
+ recovery_frame_cnt = get_ue_golomb(gb);
+ exact_match_flag = get_bits1(gb);
+ broken_link_flag = get_bits1(gb);
+ changing_group_slice_idc = get_bits(gb, 2);
+ au_contains_keyframe_message = (recovery_frame_cnt == 0);
+ return;
+
+ default:
+ skip_bits(gb, size * 8);
+ break;
+ }
+ }
}
void H264Parser::vui_parameters(GetBitContext * gb)
@@ -1015,7 +1209,7 @@ void H264Parser::vui_parameters(GetBitContext * gb)
uint H264Parser::frameRate(void) const
{
- uint64_t num;
+ uint64_t num;
uint64_t fps;
num = 500 * (uint64_t)timeScale; /* 1000 * 0.5 */
diff --git a/mythtv/libs/libmythtv/mpeg/H264Parser.h b/mythtv/libs/libmythtv/mpeg/H264Parser.h
index 2117a96..260681c 100644
--- a/mythtv/libs/libmythtv/mpeg/H264Parser.h
+++ b/mythtv/libs/libmythtv/mpeg/H264Parser.h
@@ -23,6 +23,7 @@
#ifndef H264PARSER_H
#define H264PARSER_H
+#include <QString>
#include <stdint.h>
#include "mythconfig.h"
#include "compat.h" // for uint on Darwin, MinGW
@@ -48,6 +49,10 @@ extern "C" {
class H264Parser {
public:
+ enum {
+ MAX_SLICE_HEADER_SIZE = 256
+ };
+
// ITU-T Rec. H.264 table 7-1
enum NAL_unit_type {
UNKNOWN = 0,
@@ -101,13 +106,15 @@ class H264Parser {
};
H264Parser(void);
- ~H264Parser(void) {;}
+ ~H264Parser(void) {delete [] rbsp_buffer;}
uint32_t addBytes(const uint8_t *bytes,
const uint32_t byte_count,
const uint64_t stream_offset);
void Reset(void);
+ QString NAL_type_str(uint8_t type);
+
bool stateChanged(void) const { return state_changed; }
uint8_t lastNALtype(void) const { return nal_unit_type; }
@@ -158,16 +165,21 @@ class H264Parser {
private:
enum constants {EXTENDED_SAR = 255};
- inline void set_AU_pending(const uint64_t & stream_offset)
+ inline void set_AU_pending(void)
{
if (!AU_pending)
{
AU_pending = true;
- AU_offset = stream_offset;
+ AU_offset = pkt_offset;
+ au_contains_keyframe_message = false;
}
}
bool new_AU(void);
+ void resetRBSP(void);
+ bool fillRBSP(const uint8_t *byteP, uint32_t byte_count,
+ bool found_start_code);
+ void processRBSP(bool rbsp_complete);
bool decode_Header(GetBitContext *gb);
void decode_SPS(GetBitContext *gb);
void decode_PPS(GetBitContext * gb);
@@ -177,11 +189,16 @@ class H264Parser {
bool AU_pending;
bool state_changed;
bool seen_sps;
+ bool au_contains_keyframe_message;
bool is_keyframe;
bool I_is_keyframe;
uint32_t sync_accumulator;
- GetBitContext gb;
+ uint8_t *rbsp_buffer;
+ uint32_t rbsp_buffer_size;
+ uint32_t rbsp_index;
+ uint32_t consecutive_zeros;
+ bool have_unfinished_NAL;
int prev_frame_num, frame_num;
uint slice_type;
@@ -218,7 +235,7 @@ class H264Parser {
uint32_t unitsInTick, timeScale;
bool fixedRate;
- uint64_t AU_offset, frame_start_offset, keyframe_start_offset;
+ uint64_t pkt_offset, AU_offset, frame_start_offset, keyframe_start_offset;
bool on_frame, on_key_frame;
};