Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
H264 parser: add backlog for frames without incomplete information
  • Loading branch information
perexg committed Nov 24, 2015
1 parent e6431ae commit 0b9ab57
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 29 deletions.
2 changes: 1 addition & 1 deletion src/parsers/parser_avc.c
Expand Up @@ -61,7 +61,7 @@ avc_find_startcode_internal(const uint8_t *p, const uint8_t *end)
return end + 3;
}

static const uint8_t *
const uint8_t *
avc_find_startcode(const uint8_t *p, const uint8_t *end)
{
const uint8_t *out= avc_find_startcode_internal(p, end);
Expand Down
2 changes: 2 additions & 0 deletions src/parsers/parser_avc.h
Expand Up @@ -25,6 +25,8 @@
#include "tvheadend.h"
#include "packet.h"

const uint8_t * avc_find_startcode(const uint8_t *p, const uint8_t *end);

int avc_parse_nal_units(sbuf_t *sb, const uint8_t *buf_in, int size);

int isom_write_avcc(sbuf_t *sb, const uint8_t *src, int len);
Expand Down
255 changes: 227 additions & 28 deletions src/parsers/parsers.c
Expand Up @@ -29,6 +29,7 @@
#include "service.h"
#include "parsers.h"
#include "parser_h264.h"
#include "parser_avc.h"
#include "parser_hevc.h"
#include "parser_latm.h"
#include "parser_teletext.h"
Expand All @@ -43,6 +44,21 @@
#define PARSER_SKIP 3
#define PARSER_HEADER 4

/* backlog special mask */
#define PTS_BACKLOG (PTS_MASK + 1)

static inline int
pts_is_backlog(int64_t pts)
{
return pts != PTS_UNSET && (pts & PTS_BACKLOG) != 0;
}

static inline int64_t
pts_no_backlog(int64_t pts)
{
return pts_is_backlog(pts) ? (pts & ~PTS_BACKLOG) : pts;
}

static int64_t
getpts(const uint8_t *p)
{
Expand Down Expand Up @@ -111,6 +127,12 @@ static void parser_deliver_error(service_t *t, elementary_stream_t *st);
static int parse_pes_header(service_t *t, elementary_stream_t *st,
const uint8_t *buf, size_t len);

static void parser_backlog(service_t *t, elementary_stream_t *st, th_pkt_t *pkt);

static void parser_do_backlog(service_t *t, elementary_stream_t *st,
void (*pkt_cb)(service_t *t, elementary_stream_t *st, th_pkt_t *pkt),
pktbuf_t *meta);

/**
* Parse raw mpeg data
*/
Expand Down Expand Up @@ -422,7 +444,6 @@ parse_pes(service_t *t, elementary_stream_t *st, const uint8_t *data, int len,
st->es_startcond = sc;
}


/**
*
*/
Expand Down Expand Up @@ -1228,6 +1249,68 @@ parse_mpeg2video(service_t *t, elementary_stream_t *st, size_t len,
/**
* H.264 (AVC) parser
*/
static void
parse_h264_backlog(service_t *t, elementary_stream_t *st, th_pkt_t *pkt)
{
int len, l2, pkttype = 0, isfield = 0, nal_type;
const uint8_t *end, *nal_start, *nal_end;
bitstream_t bs;
void *f;

if (pkt->pkt_payload == NULL)
return;
len = pktbuf_len(pkt->pkt_payload);
if (len <= 1)
return;
nal_start = pktbuf_ptr(pkt->pkt_payload);
end = nal_start + len;
while (1) {
while (nal_start < end && !*(nal_start++));
if (nal_start == end)
break;
nal_end = avc_find_startcode(nal_start, end);
len = nal_end - nal_start;
if (len > 3) {
nal_type = nal_start[0] & 0x1f;
if (nal_type == H264_NAL_IDR_SLICE || nal_type == H264_NAL_SLICE) {
l2 = len - 1 > 64 ? 64 : len - 1;
f = h264_nal_deescape(&bs, nal_start + 1, l2);
if(h264_decode_slice_header(st, &bs, &pkttype, &isfield))
pkttype = isfield = 0;
free(f);
if (pkttype > 0)
break;
}
}
nal_start = nal_end;
}

pkt->pkt_frametype = pkttype;
pkt->pkt_field = isfield;
pkt->pkt_duration = st->es_frame_duration;
}

static void
parse_h264_deliver(service_t *t, elementary_stream_t *st, th_pkt_t *pkt)
{
if (pkt->pkt_frametype > 0) {
if (TAILQ_EMPTY(&st->es_backlog)) {
if (pkt->pkt_frametype > 0) {
deliver:
parser_deliver(t, st, pkt);
return;
}
} else if (pkt->pkt_frametype > 0 &&
(st->es_curdts != PTS_UNSET && (st->es_curdts & PTS_BACKLOG) == 0) &&
(st->es_curpts != PTS_UNSET && (st->es_curpts & PTS_BACKLOG) == 0) &&
st->es_frame_duration > 1) {
parser_do_backlog(t, st, parse_h264_backlog, pkt ? pkt->pkt_meta : NULL);
goto deliver;
}
}
parser_backlog(t, st, pkt);
}

static int
parse_h264(service_t *t, elementary_stream_t *st, size_t len,
uint32_t next_startcode, int sc_offset)
Expand All @@ -1237,29 +1320,34 @@ parse_h264(service_t *t, elementary_stream_t *st, size_t len,
int l2, pkttype, isfield;
bitstream_t bs;
int ret = PARSER_APPEND;
th_pkt_t *pkt = st->es_curpkt;

/* delimiter - finished frame */
if ((sc & 0x1f) == H264_NAL_AUD && st->es_curpkt && st->es_curpkt->pkt_payload) {
if (st->es_curdts != PTS_UNSET && st->es_frame_duration) {
parser_deliver(t, st, st->es_curpkt);
if ((sc & 0x1f) == H264_NAL_AUD && pkt) {
if (pkt->pkt_payload == NULL || pkt->pkt_dts == PTS_UNSET) {
pkt_ref_dec(st->es_curpkt);
st->es_curpkt = NULL;
} else {
parse_h264_deliver(t, st, pkt);
st->es_curpkt = NULL;

st->es_curdts += st->es_frame_duration;
if (st->es_curpts != PTS_UNSET)
if (st->es_frame_duration < 2)
st->es_curdts |= PTS_BACKLOG;
if (st->es_curpts != PTS_UNSET) {
st->es_curpts += st->es_frame_duration;
if (st->es_frame_duration < 2)
st->es_curpts |= PTS_BACKLOG;
}
st->es_prevdts = st->es_curdts;
} else {
pkt_ref_dec(st->es_curpkt);
st->es_curpkt = NULL;
return PARSER_RESET;
}
return PARSER_RESET;
}

if(sc >= 0x000001e0 && sc <= 0x000001ef) {
/* System start codes for video */
if(len >= 9) {
uint16_t plen = buf[4] << 8 | buf[5];
th_pkt_t *pkt = st->es_curpkt;
pkt = st->es_curpkt;
if (plen >= 0xffe9) st->es_incomplete = 1;
l2 = parse_pes_header(t, st, buf + 6, len - 6);
if (l2 < 0)
Expand All @@ -1275,7 +1363,11 @@ parse_h264(service_t *t, elementary_stream_t *st, size_t len,
if (next_startcode >= 0x000001e0 && next_startcode <= 0x000001ef)
return PARSER_RESET;

parser_deliver(t, st, pkt);
if (pkt->pkt_payload == NULL || pkt->pkt_dts == PTS_UNSET) {
pkt_ref_dec(pkt);
} else {
parse_h264_deliver(t, st, pkt);
}

st->es_curpkt = NULL;
}
Expand Down Expand Up @@ -1316,24 +1408,25 @@ parse_h264(service_t *t, elementary_stream_t *st, size_t len,

case H264_NAL_IDR_SLICE:
case H264_NAL_SLICE:
if (st->es_curpkt != NULL)
break;

/* we just want the first stuff */
l2 = len - 4 > 64 ? 64 : len - 4;
void *f = h264_nal_deescape(&bs, buf + 4, l2);
/* we just want the first stuff */

if(h264_decode_slice_header(st, &bs, &pkttype, &isfield)) {
free(f);
return PARSER_RESET;
}
if(h264_decode_slice_header(st, &bs, &pkttype, &isfield))
pkttype = isfield = 0;
free(f);

if(st->es_curpkt != NULL || st->es_frame_duration == 0)
break;
if (st->es_frame_duration == 0)
st->es_frame_duration = 1;

st->es_curpkt = pkt_alloc(NULL, 0, st->es_curpts, st->es_curdts);
st->es_curpkt->pkt_frametype = pkttype;
st->es_curpkt->pkt_field = isfield;
st->es_curpkt->pkt_duration = st->es_frame_duration;
st->es_curpkt->pkt_commercial = t->s_tt_commercial_advice;
pkt = pkt_alloc(NULL, 0, st->es_curpts, st->es_curdts);
pkt->pkt_frametype = pkttype;
pkt->pkt_field = isfield;
pkt->pkt_duration = st->es_frame_duration;
pkt->pkt_commercial = t->s_tt_commercial_advice;
st->es_curpkt = pkt;
break;

default:
Expand All @@ -1346,7 +1439,7 @@ parse_h264(service_t *t, elementary_stream_t *st, size_t len,
/* Complete frame - new start code or delimiter */
if (st->es_incomplete)
return PARSER_HEADER;
th_pkt_t *pkt = st->es_curpkt;
pkt = st->es_curpkt;
size_t metalen = 0;

if(pkt != NULL && pkt->pkt_payload == NULL) {
Expand Down Expand Up @@ -1673,9 +1766,9 @@ parser_deliver(service_t *t, elementary_stream_t *st, th_pkt_t *pkt)
st->es_index,
streaming_component_type2txt(st->es_type),
pkt_frametype_to_char(pkt->pkt_frametype),
ts_rescale(pkt->pkt_pts, 1000000),
pkt->pkt_dts,
ts_rescale(pkt->pkt_dts, 1000000),
pkt->pkt_dts,
ts_rescale(pkt->pkt_pts, 1000000),
pkt->pkt_pts,
pkt->pkt_duration,
pktbuf_len(pkt->pkt_payload),
Expand Down Expand Up @@ -1719,3 +1812,109 @@ parser_deliver_error(service_t *t, elementary_stream_t *st)
parser_deliver(t, st, pkt);
st->es_buf.sb_err = 0;
}

/**
* Do backlog (fix DTS & PTS and reparse slices - frame type, field etc.)
*/
static void
parser_backlog(service_t *t, elementary_stream_t *st, th_pkt_t *pkt)
{
streaming_message_t *sm = streaming_msg_create_pkt(pkt);
TAILQ_INSERT_TAIL(&st->es_backlog, sm, sm_link);
pkt_ref_dec(pkt); /* streaming_msg_create_pkt increses ref counter */

#if ENABLE_TRACE
int64_t dts = pts_no_backlog(pkt->pkt_dts);
int64_t pts = pts_no_backlog(pkt->pkt_pts);
tvhtrace("parser",
"pkt bcklog %2d %-12s type %c"
" dts%s%10"PRId64" (%10"PRId64") pts%s%10"PRId64" (%10"PRId64")"
" dur %10d len %10zu err %i",
st->es_index,
streaming_component_type2txt(st->es_type),
pkt_frametype_to_char(pkt->pkt_frametype),
pts_is_backlog(pkt->pkt_dts) ? "+" : " ",
ts_rescale(dts, 1000000),
dts,
pts_is_backlog(pkt->pkt_pts) ? "+" : " ",
ts_rescale(pts, 1000000),
pts,
pkt->pkt_duration,
pktbuf_len(pkt->pkt_payload),
pkt->pkt_err);
#endif
}

static void
parser_do_backlog(service_t *t, elementary_stream_t *st,
void (*pkt_cb)(service_t *t, elementary_stream_t *st, th_pkt_t *pkt),
pktbuf_t *meta)
{
streaming_message_t *sm;
int64_t prevdts = PTS_UNSET, absdts = PTS_UNSET;
int64_t prevpts = PTS_UNSET, abspts = PTS_UNSET;
th_pkt_t *pkt;

tvhtrace("parser",
"pkt bcklog %2d %-12s - backlog flush start -",
st->es_index,
streaming_component_type2txt(st->es_type));
TAILQ_FOREACH(sm, &st->es_backlog, sm_link) {
pkt = sm->sm_data;
if (pkt->pkt_meta) {
meta = pkt->pkt_meta;
break;
}
}
while ((sm = TAILQ_FIRST(&st->es_backlog)) != NULL) {
pkt = sm->sm_data;
TAILQ_REMOVE(&st->es_backlog, sm, sm_link);

if (pkt->pkt_dts != PTS_UNSET)
pkt->pkt_dts &= ~PTS_BACKLOG;
if (pkt->pkt_pts != PTS_UNSET)
pkt->pkt_pts &= ~PTS_BACKLOG;

if (prevdts != PTS_UNSET && pkt->pkt_dts != PTS_UNSET &&
pkt->pkt_dts == ((prevdts + 1) & PTS_MASK))
pkt->pkt_dts = absdts;
if (prevpts != PTS_UNSET && pkt->pkt_pts != PTS_UNSET &&
pkt->pkt_pts == ((prevpts + 1) & PTS_MASK))
pkt->pkt_pts = abspts;

if (meta) {
if (pkt->pkt_meta == NULL) {
pktbuf_ref_inc(meta);
pkt->pkt_meta = meta;
}
meta = NULL;
}

pkt_cb(t, st, pkt);

if (absdts == PTS_UNSET) {
absdts = pkt->pkt_dts;
} else {
absdts += st->es_frame_duration;
absdts &= PTS_MASK;
}

if (abspts == PTS_UNSET) {
abspts = pkt->pkt_pts;
} else {
abspts += st->es_frame_duration;
abspts &= PTS_MASK;
}

prevdts = pkt->pkt_dts;
prevpts = pkt->pkt_pts;

parser_deliver(t, st, pkt);
sm->sm_data = NULL;
streaming_msg_free(sm);
}
tvhtrace("parser",
"pkt bcklog %2d %-12s - backlog flush end -",
st->es_index,
streaming_component_type2txt(st->es_type));
}
4 changes: 4 additions & 0 deletions src/service.c
Expand Up @@ -232,6 +232,8 @@ stream_init(elementary_stream_t *st)
st->es_prevdts = PTS_UNSET;

st->es_blank = 0;

TAILQ_INIT(&st->es_backlog);
}


Expand All @@ -246,6 +248,8 @@ stream_clean(elementary_stream_t *st)

/* Clear reassembly buffers */

streaming_queue_clear(&st->es_backlog);

st->es_startcode = 0;

sbuf_free(&st->es_buf);
Expand Down
1 change: 1 addition & 0 deletions src/service.h
Expand Up @@ -90,6 +90,7 @@ typedef struct elementary_stream {
int es_global_data_len;

struct th_pkt *es_curpkt;
struct streaming_message_queue es_backlog;
int64_t es_curpts;
int64_t es_curdts;
int64_t es_prevdts;
Expand Down

0 comments on commit 0b9ab57

Please sign in to comment.