From f5254a66fa0591570c080760fe5193be5aaa489d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 14 Jun 2010 12:32:57 +0000 Subject: [PATCH] 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 --- src/dvr/dvr_rec.c | 2 + src/htsp.c | 2 + src/packet.c | 32 +++++++ src/packet.h | 5 ++ src/parsers.c | 211 +++++++++++++++++++++++++++++----------------- src/rtsp.c | 2 + src/transports.c | 4 + src/tvhead.h | 4 +- 8 files changed, 182 insertions(+), 80 deletions(-) diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index c20e9baf..3c5d7497 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -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; diff --git a/src/htsp.c b/src/htsp.c index a3bf5958..65cacf05 100644 --- a/src/htsp.c +++ b/src/htsp.c @@ -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. diff --git a/src/packet.c b/src/packet.c index 1d792d16..b9740a9d 100644 --- a/src/packet.c +++ b/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; +} diff --git a/src/packet.h b/src/packet.h index e53e3827..e2cffeab 100644 --- a/src/packet.h +++ b/src/packet.h @@ -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_ */ diff --git a/src/parsers.c b/src/parsers.c index c9dfadf3..59f3915f 100644 --- a/src/parsers.c +++ b/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; } /** diff --git a/src/rtsp.c b/src/rtsp.c index 4f9be83c..54876d67 100644 --- a/src/rtsp.c +++ b/src/rtsp.c @@ -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; diff --git a/src/transports.c b/src/transports.c index c69421f5..9d574ecb 100644 --- a/src/transports.c +++ b/src/transports.c @@ -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); diff --git a/src/tvhead.h b/src/tvhead.h index f51478c4..6baad716 100644 --- a/src/tvhead.h +++ b/src/tvhead.h @@ -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) */