Splice "global data" (sequence headers for MPEG2 and SPS/PPS for h264) into

packet buffers of its own.

Make all subscribers merge it back for now. We will need this for correctly
writing global headers in MKV files. 

Ticket #61
This commit is contained in:
Andreas Öman 2010-06-14 12:32:57 +00:00
parent 2a9e1169f4
commit f5254a66fa
8 changed files with 182 additions and 80 deletions

View file

@ -595,6 +595,8 @@ dvr_thread_new_pkt(dvr_entry_t *de, th_pkt_t *pkt)
char txt[100];
int64_t pts, dts;
pkt = pkt_merge_global(pkt);
LIST_FOREACH(drs, &de->de_streams, drs_link)
if(drs->drs_source_index == pkt->pkt_componentindex)
break;

View file

@ -1413,6 +1413,8 @@ htsp_stream_deliver(htsp_subscription_t *hs, th_pkt_t *pkt)
htsmsg_add_s64(m, "pts", pts);
htsmsg_add_u32(m, "duration", dur);
pkt = pkt_merge_global(pkt);
/**
* Since we will serialize directly we use 'binptr' which is a binary
* object that just points to data, thus avoiding a copy.

View file

@ -29,6 +29,7 @@ static void
pkt_destroy(th_pkt_t *pkt)
{
free(pkt->pkt_payload);
free(pkt->pkt_globaldata);
free(pkt);
}
@ -100,3 +101,34 @@ pktref_clear_queue(struct th_pktref_queue *q)
free(pr);
}
}
/**
*
*/
th_pkt_t *
pkt_merge_global(th_pkt_t *pkt)
{
th_pkt_t *n;
if(pkt->pkt_globaldata == NULL)
return pkt;
n = malloc(sizeof(th_pkt_t));
*n = *pkt;
n->pkt_refcount = 1;
n->pkt_globaldata = NULL;
n->pkt_globaldata_len = 0;
n->pkt_payloadlen = pkt->pkt_globaldata_len + pkt->pkt_payloadlen;
n->pkt_payload = malloc(n->pkt_payloadlen + FF_INPUT_BUFFER_PADDING_SIZE);
memcpy(n->pkt_payload, pkt->pkt_globaldata, pkt->pkt_globaldata_len);
memcpy(n->pkt_payload + pkt->pkt_globaldata_len, pkt->pkt_payload,
pkt->pkt_payloadlen);
pkt_ref_dec(pkt);
return n;
}

View file

@ -41,6 +41,9 @@ typedef struct th_pkt {
uint8_t *pkt_payload;
int pkt_payloadlen;
uint8_t *pkt_globaldata;
int pkt_globaldata_len;
} th_pkt_t;
@ -66,4 +69,6 @@ void pktref_clear_queue(struct th_pktref_queue *q);
th_pkt_t *pkt_alloc(void *data, size_t datalen, int64_t pts, int64_t dts);
th_pkt_t *pkt_merge_global(th_pkt_t *pkt);
#endif /* PACKET_H_ */

View file

@ -293,7 +293,7 @@ parse_video(th_transport_t *t, th_stream_t *st, const uint8_t *data, int len,
if((sc & 0xffffff00) != 0x00000100)
continue;
r = st->st_buffer_ptr - 4;
r = st->st_buffer_ptr - st->st_startcode_offset - 4;
if(r > 0 && st->st_startcode != 0) {
r = vp(t, st, r, sc, st->st_startcode_offset);
@ -301,17 +301,29 @@ parse_video(th_transport_t *t, th_stream_t *st, const uint8_t *data, int len,
r = 1;
}
if(r) {
/* Reset packet parser upon length error or if parser
tells us so */
st->st_buffer_ptr = 0;
if(r == 2) {
// Drop packet
st->st_buffer_ptr = st->st_startcode_offset;
st->st_buffer[st->st_buffer_ptr++] = sc >> 24;
st->st_buffer[st->st_buffer_ptr++] = sc >> 16;
st->st_buffer[st->st_buffer_ptr++] = sc >> 8;
st->st_buffer[st->st_buffer_ptr++] = sc >> 0;
st->st_buffer[st->st_buffer_ptr++] = sc;
st->st_startcode = sc;
} else {
if(r == 1) {
/* Reset packet parser upon length error or if parser
tells us so */
st->st_buffer_ptr = 0;
st->st_buffer[st->st_buffer_ptr++] = sc >> 24;
st->st_buffer[st->st_buffer_ptr++] = sc >> 16;
st->st_buffer[st->st_buffer_ptr++] = sc >> 8;
st->st_buffer[st->st_buffer_ptr++] = sc;
}
st->st_startcode = sc;
st->st_startcode_offset = st->st_buffer_ptr - 4;
}
st->st_startcode = sc;
st->st_startcode_offset = st->st_buffer_ptr - 4;
}
st->st_startcond = sc;
@ -676,6 +688,23 @@ parse_mpeg2video_seq_start(th_transport_t *t, th_stream_t *st,
}
/**
*
*/
static void
parser_global_data_move(th_stream_t *st, const uint8_t *data, size_t len)
{
st->st_global_data = realloc(st->st_global_data,
st->st_global_data_len + len +
FF_INPUT_BUFFER_PADDING_SIZE);
memcpy(st->st_global_data + st->st_global_data_len, data, len);
st->st_global_data_len += len;
st->st_buffer_ptr -= len;
}
/**
* MPEG2VIDEO specific reassembly
*
@ -724,41 +753,48 @@ parse_mpeg2video(th_transport_t *t, th_stream_t *st, size_t len,
/* Sequence start code */
if(parse_mpeg2video_seq_start(t, st, &bs))
return 1;
break;
parser_global_data_move(st, buf, len);
return 2;
case 0x000001b5:
if(len < 5)
return 1;
switch(buf[4] >> 4) {
case 0x1:
/* sequence extension */
// printf("Sequence extension, len = %d\n", len);
if(len < 10)
return 1;
// printf("Profile = %d\n", buf[4] & 0x7);
// printf(" Level = %d\n", buf[5] >> 4);
break;
// Sequence Extension
parser_global_data_move(st, buf, len);
return 2;
case 0x2:
// Sequence Display Extension
parser_global_data_move(st, buf, len);
return 2;
}
break;
case 0x00000101 ... 0x000001af:
/* Slices */
if(next_startcode == 0x100 || next_startcode > 0x1af) {
/* Last picture slice (because next not a slice) */
if(st->st_curpkt == NULL) {
th_pkt_t *pkt = st->st_curpkt;
if(pkt == NULL) {
/* no packet, may've been discarded by sanity checks here */
return 1;
}
st->st_curpkt->pkt_payload = st->st_buffer;
st->st_curpkt->pkt_payloadlen = st->st_buffer_ptr - 4;
st->st_curpkt->pkt_duration = st->st_frame_duration;
if(st->st_global_data) {
pkt->pkt_globaldata = st->st_global_data;
pkt->pkt_globaldata_len = st->st_global_data_len;
st->st_global_data = NULL;
st->st_global_data_len = 0;
}
parse_compute_pts(t, st, st->st_curpkt);
pkt->pkt_payload = st->st_buffer;
pkt->pkt_payloadlen = st->st_buffer_ptr - 4;
pkt->pkt_duration = st->st_frame_duration;
parse_compute_pts(t, st, pkt);
st->st_curpkt = NULL;
st->st_buffer = malloc(st->st_buffer_size);
@ -772,6 +808,15 @@ parse_mpeg2video(th_transport_t *t, th_stream_t *st, size_t len,
}
break;
case 0x000001b8:
// GOP header
parser_global_data_move(st, buf, len);
return 2;
case 0x000001b2:
// User data
break;
default:
break;
}
@ -790,20 +835,12 @@ parse_h264(th_transport_t *t, th_stream_t *st, size_t len,
uint8_t *buf = st->st_buffer + sc_offset;
uint32_t sc = st->st_startcode;
int64_t d;
int l2, pkttype, l;
int l2, pkttype;
bitstream_t bs;
if(sc == 0x10c) {
/* RBSP padding, we don't want this */
l = len - sc_offset;
memcpy(buf, buf + l, 4); /* Move down new start code */
st->st_buffer_ptr -= l; /* Drop buffer */
}
int ret = 0;
if(sc >= 0x000001e0 && sc <= 0x000001ef) {
/* System start codes for video */
if(len >= 9)
parse_pes_header(t, st, buf + 6, len - 6);
@ -819,68 +856,84 @@ parse_h264(th_transport_t *t, th_stream_t *st, size_t len,
bs.data = NULL;
switch(sc & 0x1f) {
if(sc == 0x10c) {
// Padding
case 7:
h264_nal_deescape(&bs, buf + 3, len - 3);
if(h264_decode_seq_parameter_set(st, &bs)) {
free(bs.data);
return 1;
}
break;
st->st_buffer_ptr -= len;
ret = 2;
case 8:
h264_nal_deescape(&bs, buf + 3, len - 3);
if(h264_decode_pic_parameter_set(st, &bs)) {
free(bs.data);
return 1;
}
break;
} else {
switch(sc & 0x1f) {
case 5: /* IDR+SLICE */
case 1:
if(st->st_curpkt != NULL || st->st_frame_duration == 0 ||
st->st_curdts == AV_NOPTS_VALUE)
case 7:
h264_nal_deescape(&bs, buf + 3, len - 3);
h264_decode_seq_parameter_set(st, &bs);
parser_global_data_move(st, buf, len);
ret = 2;
break;
if(t->tht_dts_start == AV_NOPTS_VALUE)
t->tht_dts_start = st->st_curdts;
case 8:
l2 = len - 3 > 64 ? 64 : len - 3;
h264_nal_deescape(&bs, buf + 3, l2); /* we just the first stuff */
if(h264_decode_slice_header(st, &bs, &pkttype)) {
free(bs.data);
return 1;
h264_nal_deescape(&bs, buf + 3, len - 3);
h264_decode_pic_parameter_set(st, &bs);
parser_global_data_move(st, buf, len);
ret = 2;
break;
case 5: /* IDR+SLICE */
case 1:
if(st->st_curpkt != NULL || st->st_frame_duration == 0 ||
st->st_curdts == AV_NOPTS_VALUE)
break;
if(t->tht_dts_start == AV_NOPTS_VALUE)
t->tht_dts_start = st->st_curdts;
l2 = len - 3 > 64 ? 64 : len - 3;
h264_nal_deescape(&bs, buf + 3, l2); /* we just want the first stuff */
if(h264_decode_slice_header(st, &bs, &pkttype)) {
free(bs.data);
return 1;
}
st->st_curpkt = pkt_alloc(NULL, 0, st->st_curpts, st->st_curdts);
st->st_curpkt->pkt_frametype = pkttype;
st->st_curpkt->pkt_duration = st->st_frame_duration;
st->st_curpkt->pkt_commercial = t->tht_tt_commercial_advice;
break;
default:
break;
}
st->st_curpkt = pkt_alloc(NULL, 0, st->st_curpts, st->st_curdts);
st->st_curpkt->pkt_frametype = pkttype;
st->st_curpkt->pkt_duration = st->st_frame_duration;
st->st_curpkt->pkt_commercial = t->tht_tt_commercial_advice;
break;
default:
break;
}
free(bs.data);
if(next_startcode >= 0x000001e0 && next_startcode <= 0x000001ef) {
/* Complete frame */
if(st->st_curpkt == NULL)
return 1;
st->st_curpkt->pkt_payload = st->st_buffer;
st->st_curpkt->pkt_payloadlen = st->st_buffer_ptr;
parser_deliver(t, st, st->st_curpkt, 1);
th_pkt_t *pkt = st->st_curpkt;
st->st_curpkt = NULL;
st->st_buffer = malloc(st->st_buffer_size);
if(pkt != NULL) {
if(st->st_global_data) {
pkt->pkt_globaldata = st->st_global_data;
pkt->pkt_globaldata_len = st->st_global_data_len;
st->st_global_data = NULL;
st->st_global_data_len = 0;
}
pkt->pkt_payload = st->st_buffer;
pkt->pkt_payloadlen = st->st_buffer_ptr - 4;
parser_deliver(t, st, pkt, 1);
st->st_curpkt = NULL;
st->st_buffer = malloc(st->st_buffer_size);
}
return 1;
}
return 0;
return ret;
}
/**

View file

@ -391,6 +391,8 @@ rtsp_streaming_send(rtsp_t *rtsp, th_pkt_t *pkt)
{
rtsp_stream_t *rs;
pkt = pkt_merge_global(pkt);
LIST_FOREACH(rs, &rtsp->rtsp_streams, rs_link)
if(rs->rs_index == pkt->pkt_componentindex)
break;

View file

@ -154,6 +154,10 @@ stream_clean(th_stream_t *st)
st->st_curpkt = NULL;
}
free(st->st_global_data);
st->st_global_data = NULL;
st->st_global_data_len = 0;
/* Clear PTS queue */
pktref_clear_queue(&st->st_ptsq);

View file

@ -415,6 +415,8 @@ typedef struct th_stream {
int st_buffer2_ptr;
int st_buffer2_size;
uint8_t *st_global_data;
int st_global_data_len;
struct th_pkt *st_curpkt;
int64_t st_curpts;
@ -426,7 +428,7 @@ typedef struct th_stream {
int st_height;
int st_meta_change;
/* DTS generator */
int64_t st_dts_epoch; /* upper bits (auto generated) */