mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-20 15:40:14 +00:00
lavd: mitigate flood of errors at H.264 dec start
Do not pass decoder frames until one beginning with SPS is received - this mitigates flood of errors at the beginning of H.264 decode, which may be potentially harmful because it pushes off other messages (including warnings).
This commit is contained in:
@@ -81,8 +81,8 @@ int fill_coded_frame_from_sps(struct video_frame *rx_data, unsigned char *data,
|
||||
* @retval H.264 or RTP NAL type
|
||||
*/
|
||||
static uint8_t process_nal(uint8_t nal, struct video_frame *frame, uint8_t *data, int data_len) {
|
||||
uint8_t type = nal & 0x1f;
|
||||
uint8_t nri = (nal & 0x60) >> 5;
|
||||
uint8_t type = NALU_HDR_GET_TYPE(nal);
|
||||
uint8_t nri = NALU_HDR_GET_NRI(nal);
|
||||
log_msg(LOG_LEVEL_DEBUG2, "NAL type %d (nri: %d)\n", (int) type, (int) nri);
|
||||
|
||||
if (type == NAL_SPS) {
|
||||
@@ -102,7 +102,7 @@ static uint8_t process_nal(uint8_t nal, struct video_frame *frame, uint8_t *data
|
||||
static _Bool decode_nal_unit(struct video_frame *frame, int *total_length, int pass, unsigned char **dst, uint8_t *data, int data_len) {
|
||||
int fu_length = 0;
|
||||
uint8_t nal = data[0];
|
||||
uint8_t type = pass == 0 ? process_nal(nal, frame, data, data_len) : nal & 0x1f;
|
||||
uint8_t type = pass == 0 ? process_nal(nal, frame, data, data_len) : NALU_HDR_GET_TYPE(nal);
|
||||
if (type >= NAL_MIN && type <= NAL_MAX) {
|
||||
type = H264_NAL;
|
||||
}
|
||||
@@ -132,7 +132,7 @@ static _Bool decode_nal_unit(struct video_frame *frame, int *total_length, int p
|
||||
data += 2;
|
||||
data_len -= 2;
|
||||
|
||||
log_msg(LOG_LEVEL_DEBUG2, "STAP-A subpacket NAL type %d (nri: %d)\n", (int) (data[0] & 0x1f), (int) ((nal & 0x60) >> 5));
|
||||
log_msg(LOG_LEVEL_DEBUG2, "STAP-A subpacket NAL type %d (nri: %d)\n", (int) NALU_HDR_GET_TYPE(data[0]), (int) NALU_HDR_GET_NRI(nal));
|
||||
|
||||
if (nal_size <= data_len) {
|
||||
if (pass == 0) {
|
||||
@@ -181,7 +181,7 @@ static _Bool decode_nal_unit(struct video_frame *frame, int *total_length, int p
|
||||
uint8_t fu_header = *data;
|
||||
uint8_t start_bit = fu_header >> 7;
|
||||
uint8_t end_bit = (fu_header & 0x40) >> 6;
|
||||
uint8_t nal_type = fu_header & 0x1f;
|
||||
uint8_t nal_type = NALU_HDR_GET_TYPE(fu_header);
|
||||
uint8_t reconstructed_nal;
|
||||
|
||||
// Reconstruct this packet's true nal; only the data follows.
|
||||
|
||||
@@ -64,6 +64,9 @@ struct decode_data_h264 {
|
||||
|
||||
struct coded_data;
|
||||
|
||||
#define NALU_HDR_GET_TYPE(nal) ((nal) & 0x1FU)
|
||||
#define NALU_HDR_GET_NRI(nal) (((nal) & 0x60U) >> 5U)
|
||||
|
||||
int decode_frame_h264(struct coded_data *cdata, void *decode_data);
|
||||
int width_height_from_SDP(int *widthOut, int *heightOut , unsigned char *data, int data_len);
|
||||
|
||||
|
||||
@@ -158,6 +158,53 @@ long rtpenc_h264_frame_parse(struct rtpenc_h264_state *rtpench264state, unsigned
|
||||
return curNALSize(rtpench264state);
|
||||
}
|
||||
|
||||
static uint32_t get4Bytes(const unsigned char *ptr) {
|
||||
return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
|
||||
}
|
||||
|
||||
static const unsigned char *get_next_nal(const unsigned char *start, long len, _Bool with_start_code) {
|
||||
const unsigned char * const stop = start + len;
|
||||
while (stop - start >= 4) {
|
||||
uint32_t next4Bytes = get4Bytes(start);
|
||||
if (next4Bytes == 0x00000001) {
|
||||
return start + (with_start_code ? 0 : 4);
|
||||
}
|
||||
if ((next4Bytes & 0xFFFFFF00) == 0x00000100) {
|
||||
return start + (with_start_code ? 0 : 3);
|
||||
}
|
||||
// We save at least some of "next4Bytes".
|
||||
if ((unsigned) (next4Bytes & 0xFF) > 1) {
|
||||
// Common case: 0x00000001 or 0x000001 definitely doesn't begin anywhere in "next4Bytes", so we save all of it:
|
||||
start += 4;
|
||||
} else {
|
||||
// Save the first byte, and continue testing the rest:
|
||||
start += 1;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns pointer to next NAL unit in stream (excluding start code).
|
||||
*
|
||||
* @param start start of the buffer
|
||||
* @param len length of the buffer
|
||||
* @param endptr pointer to store the end of the NAL unit; may be NULL
|
||||
* @returns NAL unit beginning or NULL if no further NAL unit was found
|
||||
*/
|
||||
const unsigned char *rtpenc_h264_get_next_nal(const unsigned char *start, long len, const unsigned char **endptr) {
|
||||
const unsigned char *nal = get_next_nal(start, len, 0);
|
||||
if (endptr == NULL) {
|
||||
return nal;
|
||||
}
|
||||
if (nal == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
const unsigned char *end = get_next_nal(nal, len - (nal - start), 1);
|
||||
*endptr = end ? end : start + len;
|
||||
return nal;
|
||||
}
|
||||
|
||||
static bool rtpenc_h264_have_seen_eof(struct rtpenc_h264_state *rtpench264state) {
|
||||
return rtpench264state->haveSeenEOF;
|
||||
}
|
||||
@@ -167,7 +214,7 @@ static uint32_t test4Bytes(struct rtpenc_h264_state *rtpench264state) {
|
||||
checkEndOfFrame(rtpench264state, 4);
|
||||
|
||||
unsigned char const* ptr = nextToParse(rtpench264state);
|
||||
return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
|
||||
return get4Bytes(ptr);
|
||||
}
|
||||
static unsigned char* startOfFrame(struct rtpenc_h264_state *rtpench264state) {
|
||||
return rtpench264state->startOfFrame;
|
||||
|
||||
@@ -61,6 +61,7 @@ struct rtpenc_h264_state;
|
||||
// functions documented at definition
|
||||
struct rtpenc_h264_state *rtpenc_h264_init_state(void *buf, unsigned char *buf_in, long size);
|
||||
long rtpenc_h264_frame_parse(struct rtpenc_h264_state *rtpench264state, unsigned char **start, bool *last);
|
||||
const unsigned char *rtpenc_h264_get_next_nal(const unsigned char *start, long len, const unsigned char **endptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
#include "libavcodec/from_lavc_vid_conv.h"
|
||||
#include "lib_common.h"
|
||||
#include "tv.h"
|
||||
#include "rtp/rtpdec_h264.h"
|
||||
#include "rtp/rtpenc_h264.h"
|
||||
#include "utils/misc.h" // get_cpu_core_count()
|
||||
#include "utils/worker.h"
|
||||
#include "video.h"
|
||||
@@ -99,6 +101,8 @@ struct state_libavcodec_decompress {
|
||||
} sws;
|
||||
|
||||
struct hw_accel_state hwaccel;
|
||||
|
||||
_Bool h264_sps_found; ///< to avoid initial error flood, start decoding after SPS was received
|
||||
};
|
||||
|
||||
static enum AVPixelFormat get_format_callback(struct AVCodecContext *s, const enum AVPixelFormat *fmt);
|
||||
@@ -805,6 +809,43 @@ static int change_pixfmt(AVFrame *frame, unsigned char *dst, int av_codec, codec
|
||||
#endif // HAVE_SWSCALE
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles beginning of H.264 stream that usually floods terminal
|
||||
* output with errors because it usually doesn't start with IDR frame (even if
|
||||
* it does, codec probing swallows this). As a workaround, we wait until first
|
||||
* SPS NAL unit to avoid initial decoding errors.
|
||||
*
|
||||
* A drawback may be that it can in theory happen that the SPS NAL unit is not
|
||||
* at the beginning of the buffer, but it is not the case of libx264 and
|
||||
* hopefully neither other decoders (if so, it needs to be reworked/removed).
|
||||
*/
|
||||
static _Bool check_first_h264_sps(struct state_libavcodec_decompress *s, unsigned char *src, unsigned int src_len) {
|
||||
if (s->h264_sps_found) {
|
||||
return 1;
|
||||
}
|
||||
_Thread_local static time_ns_t t0;
|
||||
if (t0 == 0) {
|
||||
t0 = get_time_in_ns();
|
||||
}
|
||||
if (get_time_in_ns() - t0 > 10 * NS_IN_SEC) { // after 10 seconds surrender and let decoder do the job
|
||||
log_msg(LOG_LEVEL_WARNING, MOD_NAME "No SPS found, starting decode, anyway. Please report a bug to " PACKAGE_BUGREPORT " if decoding succeeds from now.\n");
|
||||
s->h264_sps_found = 1;
|
||||
return 1;
|
||||
}
|
||||
const unsigned char *first_nal = rtpenc_h264_get_next_nal(src, src_len, NULL);
|
||||
if (!first_nal) {
|
||||
return 0;
|
||||
}
|
||||
int type = NALU_HDR_GET_TYPE(first_nal[0]);
|
||||
if (type == NAL_SPS || type == NAL_SEI) {
|
||||
log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "Received H.264 SPS NALU, decoding begins...\n");
|
||||
s->h264_sps_found = 1;
|
||||
return 1;
|
||||
}
|
||||
log_msg(LOG_LEVEL_WARNING, MOD_NAME "Waiting for first H.264 SPS NALU.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static decompress_status libavcodec_decompress(void *state, unsigned char *dst, unsigned char *src,
|
||||
unsigned int src_len, int frame_seq, struct video_frame_callbacks *callbacks, codec_t *internal_codec)
|
||||
{
|
||||
@@ -812,6 +853,10 @@ static decompress_status libavcodec_decompress(void *state, unsigned char *dst,
|
||||
int got_frame = 0;
|
||||
decompress_status res = DECODER_NO_FRAME;
|
||||
|
||||
if (s->desc.color_spec == H264 && !check_first_h264_sps(s, src, src_len)) {
|
||||
return DECODER_NO_FRAME;
|
||||
}
|
||||
|
||||
if (libav_codec_has_extradata(s->desc.color_spec)) {
|
||||
int extradata_size = *(uint32_t *)(void *) src;
|
||||
if (s->codec_ctx == NULL) {
|
||||
|
||||
Reference in New Issue
Block a user