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:
parent
2a9e1169f4
commit
f5254a66fa
8 changed files with 182 additions and 80 deletions
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
32
src/packet.c
32
src/packet.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
211
src/parsers.c
211
src/parsers.c
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) */
|
||||
|
|
Loading…
Add table
Reference in a new issue