From d659274b50540b78570ac78a22efbdac7f7cc71c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Tue, 5 Feb 2008 05:58:48 +0000 Subject: [PATCH] Rewrite transport stream multiplexer and add decent parsers for all sources. We can now produce a compliant TSMUX output that plays on low-end systems. Some fallout from this (which is yet to be fixed): h264, v4l and avgen --- Makefile | 4 +- avgen.c | 1 - buffer.c | 8 +- file_input.c | 73 ++-- mux.c | 14 +- parser_h264.c | 238 +++++++++++++ parser_h264.h | 26 ++ parsers.c | 811 +++++++++++++++++++++++++++++++++++++++++++++ pes.h => parsers.h | 16 +- pes.c | 433 ------------------------ psi.c | 5 + transports.c | 22 +- tsdemux.c | 4 +- tsmux.c | 694 ++++++++++++++++++++------------------ tsmux.h | 18 +- tvhead.h | 98 ++++-- v4l.c | 3 +- 17 files changed, 1607 insertions(+), 861 deletions(-) create mode 100644 parser_h264.c create mode 100644 parser_h264.h create mode 100644 parsers.c rename pes.h => parsers.h (73%) delete mode 100644 pes.c diff --git a/Makefile b/Makefile index baf25b80..cf3682f3 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ -include ../config.mak SRCS = main.c dispatch.c channels.c transports.c teletext.c psi.c \ - subscriptions.c mux.c tsdemux.c pes.c buffer.c tcp.c \ - resolver.c tsmux.c + subscriptions.c mux.c tsdemux.c buffer.c tcp.c \ + resolver.c tsmux.c parsers.c bitstream.c parser_h264.c SRCS += http.c htmlui.c diff --git a/avgen.c b/avgen.c index a271f930..fdd17baa 100644 --- a/avgen.c +++ b/avgen.c @@ -42,7 +42,6 @@ #include "channels.h" #include "dispatch.h" #include "transports.h" -#include "pes.h" #include "buffer.h" diff --git a/buffer.c b/buffer.c index 6afc0397..9daab0f2 100644 --- a/buffer.c +++ b/buffer.c @@ -130,9 +130,11 @@ pkt_alloc(void *data, size_t datalen, int64_t pts, int64_t dts) pkt = calloc(1, sizeof(th_pkt_t)); pkt->pkt_payloadlen = datalen; - pkt->pkt_payload = malloc(datalen); - if(data != NULL) - memcpy(pkt->pkt_payload, data, datalen); + if(datalen > 0) { + pkt->pkt_payload = malloc(datalen); + if(data != NULL) + memcpy(pkt->pkt_payload, data, datalen); + } pkt->pkt_dts = dts; pkt->pkt_pts = pts; diff --git a/file_input.c b/file_input.c index 4e5cafc2..db211e8e 100644 --- a/file_input.c +++ b/file_input.c @@ -42,7 +42,7 @@ #include "channels.h" #include "dispatch.h" #include "transports.h" -#include "pes.h" +#include "parsers.h" #include "buffer.h" @@ -50,7 +50,6 @@ typedef struct file_input { AVFormatContext *fi_fctx; const char *fi_name; dtimer_t fi_timer; - int64_t fi_initial_dts; int64_t fi_refclock; int64_t fi_last_dts; int64_t fi_dts_offset; @@ -81,6 +80,7 @@ file_input_init(void) AVCodecContext *ctx; config_entry_t *ce; th_transport_t *t; + th_stream_t *st; th_channel_t *ch; TAILQ_FOREACH(ce, &config_list, ce_link) { @@ -123,10 +123,12 @@ file_input_init(void) switch(ctx->codec_id) { case CODEC_ID_H264: transport_add_stream(t, i, HTSTV_H264); - break; - + break; + case CODEC_ID_MPEG2VIDEO: - transport_add_stream(t, i, HTSTV_MPEG2VIDEO); + st = transport_add_stream(t, i, HTSTV_MPEG2VIDEO); + st->st_vbv_delay = -1; + st->st_vbv_size = 229376; break; case CODEC_ID_MP2: @@ -179,10 +181,10 @@ file_input_stop_feed(th_transport_t *t) static void -file_input_reset(file_input_t *fi) +file_input_reset(th_transport_t *t, file_input_t *fi) { av_seek_frame(fi->fi_fctx, -1, 0, AVSEEK_FLAG_BACKWARD); - fi->fi_initial_dts = AV_NOPTS_VALUE; + t->tht_dts_start = AV_NOPTS_VALUE; } @@ -196,9 +198,10 @@ file_input_start_feed(th_transport_t *t, unsigned int weight, int status) t->tht_status = TRANSPORT_RUNNING; - file_input_reset(fi); + file_input_reset(t, fi); fi->fi_refclock = getclock_hires(); - file_input_get_pkt(t, fi, fi->fi_refclock); + + dtimer_arm(&fi->fi_timer, fi_timer_callback, t, 1); return 0; } @@ -212,13 +215,14 @@ file_input_get_pkt(th_transport_t *t, file_input_t *fi, int64_t now) AVPacket ffpkt; th_pkt_t *pkt; th_stream_t *st; - int i; - int64_t pts, dts, d = 1; + int i, frametype; + int64_t pts, d = 1; + uint32_t sc; do { i = av_read_frame(fi->fi_fctx, &ffpkt); if(i < 0) { - file_input_reset(fi); + file_input_reset(t, fi); d = 20000; fi->fi_dts_offset += fi->fi_last_dts; break; @@ -232,35 +236,50 @@ file_input_get_pkt(th_transport_t *t, file_input_t *fi, int64_t now) continue; - if(fi->fi_initial_dts == AV_NOPTS_VALUE) - fi->fi_initial_dts = ffpkt.dts; - - ffpkt.dts -= fi->fi_initial_dts; - ffpkt.pts -= fi->fi_initial_dts; + if(t->tht_dts_start == AV_NOPTS_VALUE) + t->tht_dts_start = ffpkt.dts; pts = av_rescale_q(ffpkt.pts, fi->fi_fctx->streams[ffpkt.stream_index]->time_base, AV_TIME_BASE_Q); - dts = av_rescale_q(ffpkt.dts, - fi->fi_fctx->streams[ffpkt.stream_index]->time_base, - AV_TIME_BASE_Q); - + /* Figure frametype */ + + switch(st->st_type) { + case HTSTV_MPEG2VIDEO: + sc = ffpkt.data[0] << 24 | ffpkt.data[1] << 16 | ffpkt.data[2] << 8 | + ffpkt.data[3]; + if(sc == 0x100) { + frametype = (ffpkt.data[5] >> 3) & 7; + } else { + frametype = PKT_I_FRAME; + } + break; + default: + frametype = 0; + break; + } + pkt = pkt_alloc(ffpkt.data, ffpkt.size, - pts + fi->fi_dts_offset, dts + fi->fi_dts_offset); + ffpkt.pts + fi->fi_dts_offset, + ffpkt.dts + fi->fi_dts_offset); + pkt->pkt_frametype = frametype; pkt->pkt_stream = st; - pkt->pkt_duration = ffpkt.duration; - - fi->fi_last_dts = dts; + st->st_tb = fi->fi_fctx->streams[ffpkt.stream_index]->time_base; + fi->fi_last_dts = ffpkt.dts; avgstat_add(&st->st_rate, ffpkt.size, dispatch_clock); - pes_compute_duration(t, st, pkt); + parser_compute_duration(t, st, pkt); + + pts = av_rescale_q(ffpkt.pts - t->tht_dts_start, + fi->fi_fctx->streams[ffpkt.stream_index]->time_base, + AV_TIME_BASE_Q); av_free_packet(&ffpkt); - d = pts + fi->fi_dts_offset - 1000000 - (now - fi->fi_refclock); + d = pts - 1000000 - (now - fi->fi_refclock); } while(d <= 0); dtimer_arm_hires(&fi->fi_timer, fi_timer_callback, t, now + d); diff --git a/mux.c b/mux.c index 73b9bc4f..e3e77da6 100644 --- a/mux.c +++ b/mux.c @@ -39,7 +39,6 @@ #include "teletext.h" #include "transports.h" #include "subscriptions.h" -#include "pes.h" #include "psi.h" #include "buffer.h" @@ -150,13 +149,24 @@ muxer_init(th_subscription_t *s, th_mux_output_t *cb, void *opaque, LIST_FOREACH(st, &t->tht_streams, st_link) { + + switch(st->st_type) { + case HTSTV_MPEG2VIDEO: + case HTSTV_MPEG2AUDIO: + case HTSTV_AC3: + case HTSTV_H264: + break; + + default: + continue; + } + tms = calloc(1, sizeof(th_muxstream_t)); tms->tms_muxer = tm; tms->tms_stream = st; LIST_INSERT_HEAD(&tm->tm_streams, tms, tms_muxer_link0); - TAILQ_INIT(&tms->tms_metaqueue); } s->ths_muxer = tm; diff --git a/parser_h264.c b/parser_h264.c new file mode 100644 index 00000000..b9f69924 --- /dev/null +++ b/parser_h264.c @@ -0,0 +1,238 @@ +/* + * h264 SPS / PPS Parser + * + * Based on h264.c from ffmpeg (www.ffmpeg.org) + * + * Copyright (c) 2003 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include "tvhead.h" +#include "parsers.h" +#include "buffer.h" +#include "bitstream.h" + +#if 0 +/** + * H.264 parser, nal constructor + */ +static int +build_nal(bitstream_t *bs, uint8_t *data, int size, int maxsize) +{ + int rbsp_size, i; + + bs->data = malloc(maxsize); + + /* Escape 0x000003 into 0x0000 */ + + rbsp_size = 0; + for(i = 1; i < size; i++) { + if(i + 2 < size && data[i] == 0 && data[i + 1] == 0 && data[i + 1] == 3) { + bs->data[rbsp_size++] = 0; + bs->data[rbsp_size++] = 0; + i += 2; + } else { + bs->data[rbsp_size++] = data[i]; + } + } + + bs->offset = 0; + bs->len = rbsp_size * 8; + return 0; +} + +#endif + + +typedef struct h264_private { + + struct { + int frame_duration; + + } sps[256]; + +} h264_private_t; + + + + +static int +decode_vui(th_stream_t *st, bitstream_t *bs, int spsid) +{ + int units_in_tick; + int time_scale; + int fixed_rate; + + if(read_bits1(bs)) { + if(read_bits(bs, 8) == 255) { + read_bits(bs, 16); + read_bits(bs, 16); + } + } + + if(read_bits1(bs)){ /* overscan_info_present_flag */ + read_bits1(bs); /* overscan_appropriate_flag */ + } + + if(read_bits1(bs)){ /* video_signal_type_present_flag */ + read_bits(bs, 3); /* video_format */ + read_bits1(bs); /* video_full_range_flag */ + if(read_bits1(bs)){ /* colour_description_present_flag */ + read_bits(bs, 8); /* colour_primaries */ + read_bits(bs, 8); /* transfer_characteristics */ + read_bits(bs, 8); /* matrix_coefficients */ + } + } + + if(read_bits1(bs)){ /* chroma_location_info_present_flag */ + read_golomb_ue(bs); /* chroma_sample_location_type_top_field */ + read_golomb_ue(bs); /* chroma_sample_location_type_bottom_field */ + } + + if(!read_bits1(bs)) /* We want timing info */ + return -1; + + units_in_tick = read_bits(bs, 32); + time_scale = read_bits(bs, 32); + fixed_rate = read_bits1(bs); + + printf("units_in_tick = %d\n", units_in_tick); + printf("time_scale = %d\n", time_scale); + printf("fixed_rate = %d\n", fixed_rate); + + return 0; +} + + + +static void +decode_scaling_list(bitstream_t *bs, int size) +{ + int i, last = 8, next = 8; + if(!read_bits1(bs)) + return; /* matrix not written */ + + for(i=0;ist_parser_private; + int profile_idc, level_idc, poc_type; + unsigned int sps_id, tmp, i; + + profile_idc= read_bits(bs, 8); + read_bits1(bs); //constraint_set0_flag + read_bits1(bs); //constraint_set1_flag + read_bits1(bs); //constraint_set2_flag + read_bits1(bs); //constraint_set3_flag + read_bits(bs, 4); // reserved + level_idc= read_bits(bs, 8); + sps_id= read_golomb_ue(bs); + + printf("profile = %d\n", profile_idc); + printf("level_idc = %d\n", level_idc); + printf("sps_id = %d\n", sps_id); + + + if(profile_idc >= 100){ //high profile + if(read_golomb_ue(bs) == 3) //chroma_format_idc + read_bits1(bs); //residual_color_transform_flag + read_golomb_ue(bs); //bit_depth_luma_minus8 + read_golomb_ue(bs); //bit_depth_chroma_minus8 + read_bits1(bs); + + if(read_bits1(bs)) { + printf("scaling\n"); + /* Scaling matrices */ + decode_scaling_list(bs, 16); + decode_scaling_list(bs, 16); + decode_scaling_list(bs, 16); + decode_scaling_list(bs, 16); + decode_scaling_list(bs, 16); + decode_scaling_list(bs, 16); + + decode_scaling_list(bs, 64); + decode_scaling_list(bs, 64); + + } + } + + tmp = read_golomb_ue(bs); /* max frame num */ + printf("maxframenum = %d\n", tmp); + poc_type= read_golomb_ue(bs); + printf("poc_type = %d\n", poc_type); + + if(poc_type == 0){ //FIXME #define + read_golomb_ue(bs); + } else if(poc_type == 1){//FIXME #define + read_bits1(bs); + read_golomb_se(bs); + read_golomb_se(bs); + tmp = read_golomb_ue(bs); /* poc_cycle_length */ + for(i = 0; i < tmp; i++) + read_golomb_se(bs); + + }else if(poc_type != 2){ + printf("Illegal POC type %d\n", poc_type); + /* Illegal poc */ + return -1; + } + + tmp = read_golomb_ue(bs); + printf("reference frames = %d\n", tmp); + + read_bits1(bs); + + tmp = read_golomb_ue(bs) + 1; /* mb width */ + tmp = read_golomb_ue(bs) + 1; /* mb height */ + + if(!read_bits1(bs)) + read_bits1(bs); + + read_bits1(bs); + + /* CROP */ + if(read_bits1(bs)){ + tmp = read_golomb_ue(bs); + tmp = read_golomb_ue(bs); + tmp = read_golomb_ue(bs); + tmp = read_golomb_ue(bs); + } + + if(read_bits1(bs)) { + decode_vui(st, bs, sps_id); + return 0; + } else { + return -1; + } +} diff --git a/parser_h264.h b/parser_h264.h new file mode 100644 index 00000000..f6c40f31 --- /dev/null +++ b/parser_h264.h @@ -0,0 +1,26 @@ +/* + * Packet parsing functions + * Copyright (C) 2007 Andreas Öman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PARSER_H264_H_ +#define PARSER_H264_H_ + +#include "bitstream.h" + +int h264_decode_seq_parameter_set(th_stream_t *st, bitstream_t *bs); + +#endif /* PARSER_H264_H_ */ diff --git a/parsers.c b/parsers.c new file mode 100644 index 00000000..7e80549b --- /dev/null +++ b/parsers.c @@ -0,0 +1,811 @@ +/* + * Packet parsing functions + * Copyright (C) 2007 Andreas Öman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "tvhead.h" +#include "parsers.h" +#include "parser_h264.h" +#include "bitstream.h" +#include "buffer.h" +#include "dispatch.h" + +static const AVRational mpeg_tc = {1, 90000}; + +#define getu32(b, l) ({ \ + uint32_t x = (b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]); \ + b+=4; \ + l-=4; \ + x; \ +}) + +#define getu16(b, l) ({ \ + uint16_t x = (b[0] << 8 | b[1]); \ + b+=2; \ + l-=2; \ + x; \ +}) + +#define getu8(b, l) ({ \ + uint8_t x = b[0]; \ + b+=1; \ + l-=1; \ + x; \ +}) + +#define getpts(b, l) ({ \ + int64_t _pts; \ + _pts = (int64_t)((getu8(b, l) >> 1) & 0x07) << 30; \ + _pts |= (int64_t)(getu16(b, l) >> 1) << 15; \ + _pts |= (int64_t)(getu16(b, l) >> 1); \ + _pts; \ +}) + + +static int parse_mpeg2video(th_transport_t *t, th_stream_t *st, size_t len, + uint32_t next_startcode, int sc_offset); + +static int parse_h264(th_transport_t *t, th_stream_t *st, size_t len, + uint32_t next_startcode, int sc_offset); + +typedef int (vparser_t)(th_transport_t *t, th_stream_t *st, size_t len, + uint32_t next_startcode, int sc_offset); + +typedef void (aparser_t)(th_transport_t *t, th_stream_t *st, uint8_t *data, + int len); + +static void parse_video(th_transport_t *t, th_stream_t *st, uint8_t *data, + int len, vparser_t *vp); + +static void parse_audio(th_transport_t *t, th_stream_t *st, uint8_t *data, + int len, int start, aparser_t *vp); + +static void parse_mpegaudio(th_transport_t *t, th_stream_t *st, uint8_t *data, + int len); + +static void parse_ac3(th_transport_t *t, th_stream_t *st, uint8_t *data, + int len); + +static void parse_compute_pts(th_transport_t *t, th_stream_t *st, + th_pkt_t *pkt); + +static void parser_deliver(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt); + +static int parse_pes_header(th_transport_t *t, th_stream_t *st, uint8_t *buf, + size_t len); + +void parser_compute_duration(th_transport_t *t, th_stream_t *st, + th_pkt_t *pkt); + +/** + * Parse raw mpeg data + */ +void +parse_raw_mpeg(th_transport_t *t, th_stream_t *st, uint8_t *data, + int len, int start, int err) +{ + + if(LIST_FIRST(&t->tht_muxers) == NULL) + return; /* No muxers will take packet, so drop here */ + + switch(st->st_type) { + case HTSTV_MPEG2VIDEO: + parse_video(t, st, data, len, parse_mpeg2video); + break; + + case HTSTV_H264: + parse_video(t, st, data, len, parse_h264); + break; + + case HTSTV_MPEG2AUDIO: + parse_audio(t, st, data, len, start, parse_mpegaudio); + break; + + case HTSTV_AC3: + parse_audio(t, st, data, len, start, parse_ac3); + break; + + default: + break; + } +} + + +/** + * Generic video parser + * + * We scan for startcodes a'la 0x000001xx and let a specific parser + * derive further information. + */ +static void +parse_video(th_transport_t *t, th_stream_t *st, uint8_t *data, int len, + vparser_t *vp) +{ + + uint32_t sc; + int i, r; + + sc = st->st_startcond; + + if(st->st_buffer == NULL) { + st->st_buffer_size = 4000; + st->st_buffer = malloc(st->st_buffer_size); + } + + if(st->st_buffer_ptr + len + 4 >= st->st_buffer_size) { + st->st_buffer_size += len * 4; + st->st_buffer = realloc(st->st_buffer, st->st_buffer_size); + } + + for(i = 0; i < len; i++) { + st->st_buffer[st->st_buffer_ptr++] = data[i]; + sc = sc << 8 | data[i]; + + if((sc & 0xffffff00) != 0x00000100) + continue; + + r = st->st_buffer_ptr - 4; + + if(r > 0 && st->st_startcode != 0) { + r = vp(t, st, r, sc, st->st_startcode_offset); + } else { + r = 1; + } + + if(r) { + /* 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 >> 0; + } + st->st_startcode = sc; + st->st_startcode_offset = st->st_buffer_ptr - 4; + + } + st->st_startcond = sc; + + +} + + + +/** + * Generic audio parser + * + * We trust 'start' to be set where a new frame starts + */ +static void +parse_audio(th_transport_t *t, th_stream_t *st, uint8_t *data, + int len, int start, aparser_t *ap) +{ + if(start) { + if(st->st_buffer_ptr != 0) + ap(t, st, st->st_buffer, st->st_buffer_ptr); + + st->st_buffer_ptr = 0; + st->st_buffer_errors = 0; + + if(st->st_buffer == NULL) { + + if(st->st_buffer_size < 1000) + st->st_buffer_size = 4000; + + st->st_buffer = malloc(st->st_buffer_size); + } + } + + if(st->st_buffer == NULL) + return; + + if(st->st_buffer_ptr + len >= st->st_buffer_size) { + st->st_buffer_size += len * 4; + st->st_buffer = realloc(st->st_buffer, st->st_buffer_size); + } + + memcpy(st->st_buffer + st->st_buffer_ptr, data, len); + st->st_buffer_ptr += len; +} + + +/** + * Mpeg audio parser + */ +//const static int mpegaudio_freq_tab[4] = {44100, 48000, 32000, 0}; + +static void +parse_mpegaudio(th_transport_t *t, th_stream_t *st, uint8_t *buf, int len) +{ + int hlen, l, duration; + th_pkt_t *pkt; + + if(len < 9) + return; + if((hlen = parse_pes_header(t, st, buf + 6, len - 6)) < 0) + return; + + l = len - hlen - 6; + if(l < 0) + return; + + buf += hlen + 6; + len -= hlen + 6; +#if 0 + header = 0; + duration = 0; + + printf("audioheader %02x.%02x.%02x.%02x\n", + buf[0], + buf[1], + buf[2], + buf[3]); + + for(i = 0; i < len; i++) { + header = header << 8 | buf[i]; + + if((header & 0xffe00000) != 0xffe00000) + continue; + + if((header & (3 << 17)) != 2 << 17) + continue; + + if((header & (15 << 12)) == 15 << 12) + continue; + + if((header & (3 << 10)) == 3 << 10) + continue; + + sample_rate = mpegaudio_freq_tab[(header >> 10) & 3]; + duration += 90000 * 1152 / sample_rate; + printf("duration = %d\n", duration); + } +#endif + + pkt = pkt_alloc(buf, len, st->st_curpts, st->st_curdts); + pkt->pkt_commercial = t->tht_tt_commercial_advice; + + + parser_compute_duration(t, st, pkt); + return; + + + pkt->pkt_duration = duration; + parser_deliver(t, st, pkt); + st->st_curpts += duration; + st->st_curdts += duration; +} + + +/** + * AC3 audio parser + */ +#if 0 +const static int ac3_freq_tab[4] = {48000, 44100, 32000, 0}; + +const static uint16_t ac3_frame_size_tab[38][3] = { + { 64, 69, 96 }, + { 64, 70, 96 }, + { 80, 87, 120 }, + { 80, 88, 120 }, + { 96, 104, 144 }, + { 96, 105, 144 }, + { 112, 121, 168 }, + { 112, 122, 168 }, + { 128, 139, 192 }, + { 128, 140, 192 }, + { 160, 174, 240 }, + { 160, 175, 240 }, + { 192, 208, 288 }, + { 192, 209, 288 }, + { 224, 243, 336 }, + { 224, 244, 336 }, + { 256, 278, 384 }, + { 256, 279, 384 }, + { 320, 348, 480 }, + { 320, 349, 480 }, + { 384, 417, 576 }, + { 384, 418, 576 }, + { 448, 487, 672 }, + { 448, 488, 672 }, + { 512, 557, 768 }, + { 512, 558, 768 }, + { 640, 696, 960 }, + { 640, 697, 960 }, + { 768, 835, 1152 }, + { 768, 836, 1152 }, + { 896, 975, 1344 }, + { 896, 976, 1344 }, + { 1024, 1114, 1536 }, + { 1024, 1115, 1536 }, + { 1152, 1253, 1728 }, + { 1152, 1254, 1728 }, + { 1280, 1393, 1920 }, + { 1280, 1394, 1920 }, +}; +#endif + +static void +parse_ac3(th_transport_t *t, th_stream_t *st, uint8_t *buf, int len) +{ +#if 0 + int hlen, l, src, fsc, sample_rate, v, bsid, frame_size; + int o, duration; +#endif + + int hlen, l; + int duration; + th_pkt_t *pkt; + + if(len < 9) + return; + + if((hlen = parse_pes_header(t, st, buf + 6, len - 6)) < 0) + return; + + l = len - hlen - 6; + if(l < 0) + return; + + buf += hlen + 6; + len -= hlen + 6; + +#if 0 + duration = 0; + o = 0; + + while(o < len - 8) { + v = (buf[o] << 8) | buf[o + 1]; + if(v != 0xb77) + break; + + src = buf[o + 4] >> 6; + fsc = buf[o + 4] & 0x3f; + bsid = (buf[o + 5] & 0xf) - 8; + if(bsid < 0) + bsid = 0; + + sample_rate = ac3_freq_tab[src] >> bsid; + frame_size = ac3_frame_size_tab[fsc][src] * 2; + + o += frame_size; + duration += 90000 * 1536 / sample_rate; + break; + } +#endif + + pkt = pkt_alloc(buf, len, st->st_curpts, st->st_curdts); + pkt->pkt_commercial = t->tht_tt_commercial_advice; + parser_compute_duration(t, st, pkt); + return; + + pkt->pkt_duration = duration; + st->st_curpts += duration; + st->st_curdts += duration; + parser_deliver(t, st, pkt); +} + + + +/** + * PES header parser + * + * Extract DTS and PTS and update current values in stream + */ +static int +parse_pes_header(th_transport_t *t, th_stream_t *st, uint8_t *buf, size_t len) +{ + int64_t dts, pts; + int hdr, flags, hlen; + + hdr = getu8(buf, len); + flags = getu8(buf, len); + hlen = getu8(buf, len); + + if(len < hlen || (hdr & 0xc0) != 0x80) + return -1; + + if((flags & 0xc0) == 0xc0) { + if(hlen < 10) + return -1; + + pts = getpts(buf, len); + dts = getpts(buf, len); + + } else if((flags & 0xc0) == 0x80) { + if(hlen < 5) + return -1; + + dts = pts = getpts(buf, len); + } else + return hlen + 3; + + st->st_curdts = dts; + st->st_curpts = pts; + return hlen + 3; +} + + + +/** + * MPEG2VIDEO frame duration table (in 90kHz clock domain) + */ +const static unsigned int mpeg2video_framedurations[16] = { + 0, + 3753, + 3750, + 3600, + 3003, + 3000, + 1800, + 1501, + 1500, +}; + +/** + * MPEG2VIDEO specific reassembly + * + * Process all startcodes (also the system ones) + * + * Extract framerate (or duration to be more specific) + * + * 'steal' the st->st_buffer and use it as 'pkt' buffer + * + */ +static int +parse_mpeg2video(th_transport_t *t, th_stream_t *st, size_t len, + uint32_t next_startcode, int sc_offset) +{ + uint8_t *buf = st->st_buffer + sc_offset; + bitstream_t bs; + int v, pct; + + init_bits(&bs, buf + 4, (len - 4) * 8); + + switch(st->st_startcode) { + case 0x000001e0 ... 0x000001ef: + /* System start codes for video */ + if(len >= 9) + parse_pes_header(t, st, buf + 6, len - 6); + return 1; + + case 0x00000100: + /* Picture start code */ + if(st->st_frame_duration == 0 || st->st_curdts == AV_NOPTS_VALUE) + return 1; + + if(len < 6) + return 1; + + skip_bits(&bs, 10); /* temporal reference */ + + pct = read_bits(&bs, 3); + if(pct < PKT_I_FRAME || pct > PKT_B_FRAME) + return 1; /* Illegal picture_coding_type */ + + /* If this is the first I-frame seen, set dts_start as a reference + offset */ + if(pct == PKT_I_FRAME && t->tht_dts_start == AV_NOPTS_VALUE) + t->tht_dts_start = st->st_curdts; + + v = read_bits(&bs, 16); /* vbv_delay */ + if(v == 0xffff) + st->st_vbv_delay = -1; + else + st->st_vbv_delay = av_rescale_q(v, st->st_tb, AV_TIME_BASE_Q); + + if(st->st_curpkt != NULL) + pkt_deref(st->st_curpkt); + + st->st_curpkt = pkt_alloc(NULL, 0, st->st_curpts, st->st_curdts); + st->st_curpkt->pkt_frametype = pct; + st->st_curpkt->pkt_duration = st->st_frame_duration; + st->st_curpkt->pkt_commercial = t->tht_tt_commercial_advice; + + break; + + case 0x000001b3: + /* Sequence start code */ + if(len < 11) + return 1; + + skip_bits(&bs, 12); + skip_bits(&bs, 12); + skip_bits(&bs, 4); + st->st_frame_duration = mpeg2video_framedurations[read_bits(&bs, 4)]; + v = read_bits(&bs, 18) * 400; + skip_bits(&bs, 1); + + v = read_bits(&bs, 10) * 16 * 1024 / 8; + st->st_vbv_size = v; + break; + + 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; + } + 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) { + /* 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; + + parse_compute_pts(t, st, st->st_curpkt); + st->st_curpkt = NULL; + + st->st_buffer = malloc(st->st_buffer_size); + + /* If we know the frame duration, increase DTS accordingly */ + st->st_curdts += st->st_frame_duration; + + /* PTS cannot be extrapolated (it's not linear) */ + st->st_curpts = AV_NOPTS_VALUE; + return 1; + } + break; + + default: + break; + } + + return 0; +} + + +/** + * H.264 parser + */ +static int +parse_h264(th_transport_t *t, th_stream_t *st, size_t len, + uint32_t next_startcode, int sc_offset) +{ + uint8_t *buf = st->st_buffer + sc_offset; + uint32_t sc = st->st_startcode; + int64_t d; + + if(sc >= 0x000001e0 && sc <= 0x000001ef) { + /* System start codes for video */ + + if(len >= 9) + parse_pes_header(t, st, buf + 6, len - 6); + + if(st->st_prevdts != AV_NOPTS_VALUE) { + d = (st->st_curdts - st->st_prevdts) & 0x1ffffffffLL; + + if(d < 90000) + st->st_frame_duration = d; + } + st->st_prevdts = st->st_curdts; + return 1; + } + + printf("sc = %x\n", sc & 0x1f); + + switch(sc & 0x1f) { + + case 5: /* IDR+SLICE */ + /* IDR seen, set DTS offset if not already set */ + if(t->tht_dts_start == AV_NOPTS_VALUE) + t->tht_dts_start = st->st_curdts; + + case 1: + if(st->st_curpkt != NULL) + break; + printf("Creating pkt @ %lld %lld\n", st->st_curpts, st->st_curdts); + + st->st_curpkt = pkt_alloc(NULL, 0, st->st_curpts, st->st_curdts); + st->st_curpkt->pkt_frametype = 0; + st->st_curpkt->pkt_duration = st->st_frame_duration; + st->st_curpkt->pkt_commercial = t->tht_tt_commercial_advice; + + + break; + + + default: + break; + } + + 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); + + st->st_curpkt = NULL; + st->st_buffer = malloc(st->st_buffer_size); + return 1; + } + + return 0; +} + + + +/** + * Compute PTS (if not known) + * + * We do this by placing packets on a queue and wait for next I/P + * frame to appear + */ +static void +parse_compute_pts(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt) +{ + th_pkt_t *p; + + if(pkt->pkt_pts != AV_NOPTS_VALUE && st->st_ptsq_len == 0) { + /* PTS known and no other packets in queue, deliver at once */ + parser_deliver(t, st, pkt); + return; + } + + TAILQ_INSERT_TAIL(&st->st_ptsq, pkt, pkt_queue_link); + st->st_ptsq_len++; + + while((pkt = TAILQ_FIRST(&st->st_ptsq)) != NULL) { + switch(pkt->pkt_frametype) { + case PKT_B_FRAME: + /* B-frames have same PTS as DTS, pass them on */ + pkt->pkt_pts = pkt->pkt_dts; + break; + + case PKT_I_FRAME: + case PKT_P_FRAME: + /* Presentation occures at DTS of next I or P frame, + try to find it */ + p = TAILQ_NEXT(pkt, pkt_queue_link); + while(1) { + if(p == NULL) + return; /* not arrived yet, wait */ + if(p->pkt_frametype <= PKT_P_FRAME) { + pkt->pkt_pts = p->pkt_dts; + break; + } + p = TAILQ_NEXT(p, pkt_queue_link); + } + break; + } + + TAILQ_REMOVE(&st->st_ptsq, pkt, pkt_queue_link); + st->st_ptsq_len--; + parser_deliver(t, st, pkt); + } +} + +/** + * Compute duration of a packet, we do this by keeping a packet + * until the next one arrives, then we release it + */ +void +parser_compute_duration(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt) +{ + th_pkt_t *next; + int64_t d; + + TAILQ_INSERT_TAIL(&st->st_durationq, pkt, pkt_queue_link); + + pkt = TAILQ_FIRST(&st->st_durationq); + if((next = TAILQ_NEXT(pkt, pkt_queue_link)) == NULL) + return; + + d = next->pkt_dts - pkt->pkt_dts; + TAILQ_REMOVE(&st->st_durationq, pkt, pkt_queue_link); + pkt->pkt_duration = d; + + parser_deliver(t, st, pkt); +} + + + +/** + * De-wrap and normalize PTS/DTS to 1MHz clock domain + */ +static void +parser_deliver(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt) +{ + th_muxer_t *tm; + int64_t dts, pts, ptsoff; + + if(t->tht_dts_start == AV_NOPTS_VALUE) { + pkt_deref(pkt); + return; + } + + dts = pkt->pkt_dts; + pts = pkt->pkt_pts; + + /* Compute delta between PTS and DTS (and watch out for 33 bit wrap) */ + ptsoff = (pts - dts) & 0x1ffffffffLL; + + /* Subtract the transport wide start offset */ + dts -= t->tht_dts_start; + + if(st->st_last_dts == AV_NOPTS_VALUE) { + if(dts < 0) { + /* Early packet with negative time stamp, drop those */ + pkt_deref(pkt); + return; + } + } else if(dts + st->st_dts_epoch < st->st_last_dts - (1LL << 24)) { + /* DTS wrapped, increase upper bits */ + st->st_dts_epoch += 1ULL << 33; + } + + dts += st->st_dts_epoch; + st->st_last_dts = dts; + + pts = dts + ptsoff; + + /* Rescale to tvheadned internal 1MHz clock */ + pkt->pkt_dts =av_rescale_q(dts, st->st_tb, AV_TIME_BASE_Q); + pkt->pkt_pts =av_rescale_q(pts, st->st_tb, AV_TIME_BASE_Q); + pkt->pkt_duration=av_rescale_q(pkt->pkt_duration, st->st_tb, AV_TIME_BASE_Q); +#if 0 + printf("%20lld: %-12s %d %10lld %10lld %d\n", + getclock_hires(), + htstvstreamtype2txt(st->st_type), + pkt->pkt_frametype, + pkt->pkt_dts, + pkt->pkt_pts, + pkt->pkt_duration); +#endif + pkt->pkt_stream = st; + + /* Alert all muxers tied to us that a new packet has arrived */ + + LIST_FOREACH(tm, &t->tht_muxers, tm_transport_link) + tm->tm_new_pkt(tm, st, pkt); + + /* Unref (and possibly free) the packet, muxers are supposed + to increase refcount or copy packet if they need anything */ + + pkt_deref(pkt); +} diff --git a/pes.h b/parsers.h similarity index 73% rename from pes.h rename to parsers.h index 8a9cbce5..fcbffe41 100644 --- a/pes.h +++ b/parsers.h @@ -16,15 +16,13 @@ * along with this program. If not, see . */ -#ifndef PES_H -#define PES_H +#ifndef PARSERS_H +#define PARSERS_H -int pes_packet_input(th_transport_t *th, th_stream_t *st, uint8_t *data, - size_t len); - -void pes_compute_duration(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt); - -void pes_reassembly(th_transport_t *t, th_stream_t *st, uint8_t *data, +void parse_raw_mpeg(th_transport_t *t, th_stream_t *st, uint8_t *data, int len, int start, int err); -#endif /* PES_H */ +void parser_compute_duration(th_transport_t *t, th_stream_t *st, + th_pkt_t *pkt); + +#endif /* PARSERS_H */ diff --git a/pes.c b/pes.c deleted file mode 100644 index 4a487655..00000000 --- a/pes.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * PES parsing functions - * Copyright (C) 2007 Andreas Öman - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include - -#include -#include -#include -#include -#include -#include - -#include "tvhead.h" -#include "pes.h" -#include "dispatch.h" -#include "buffer.h" - -static void pes_compute_dts(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt); -static void pes_compute_pts(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt); - -#define getu32(b, l) ({ \ - uint32_t x = (b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]); \ - b+=4; \ - l-=4; \ - x; \ -}) - -#define getu16(b, l) ({ \ - uint16_t x = (b[0] << 8 | b[1]); \ - b+=2; \ - l-=2; \ - x; \ -}) - -#define getu8(b, l) ({ \ - uint8_t x = b[0]; \ - b+=1; \ - l-=1; \ - x; \ -}) - -#define getpts(b, l) ({ \ - int64_t _pts; \ - _pts = (int64_t)((getu8(b, l) >> 1) & 0x07) << 30; \ - _pts |= (int64_t)(getu16(b, l) >> 1) << 15; \ - _pts |= (int64_t)(getu16(b, l) >> 1); \ - _pts; \ -}) - -/** - * pes_reassembly() - * - * - */ - -void -pes_reassembly(th_transport_t *t, th_stream_t *st, uint8_t *data, - int len, int start, int err) -{ - uint32_t sc; - int i; - - if(LIST_FIRST(&t->tht_muxers) == NULL) - return; /* No muxers will take packet, so drop here */ - - sc = st->st_startcond; - - if(st->st_buffer == NULL) { - st->st_buffer_size = 4000; - st->st_buffer = malloc(st->st_buffer_size); - } - - if(st->st_buffer_ptr + len >= st->st_buffer_size) { - st->st_buffer_size += len * 4; - st->st_buffer = realloc(st->st_buffer, st->st_buffer_size); - } - - for(i = 0; i < len; i++) { - st->st_buffer[st->st_buffer_ptr++] = data[i]; - sc = sc << 8 | data[i]; - - if((sc & 0xffffff00) == 0x00000100) { - /* Startcode condition */ - switch(sc) { - case 0x000001e0 ... 0x000001ef: - case 0x000001c0 ... 0x000001cf: - case 0x000001bd: - st->st_buffer_ptr -= 4; - if(st->st_buffer_ptr < 0) { - /* Fake startcode, can be seen during stream start */ - st->st_buffer_ptr = 0; - break; - } - - if(st->st_startcode != 0) { - /* It would be nice to embed much of what's done in - * pes_packet_input() here to avoid a bunch of memcpy()s - */ - pes_packet_input(t, st, st->st_buffer + 2, st->st_buffer_ptr - 2); - st->st_buffer_ptr = 0; - } - st->st_startcode = sc; - break; - } - } - } - st->st_startcond = sc; -} - - - - -/* - * pes_packet_input() - * - * return 0 if buf-memory is claimed by us, -1 if memory can be resued - */ -int -pes_packet_input(th_transport_t *t, th_stream_t *st, uint8_t *buf, size_t len) -{ - int64_t dts = AV_NOPTS_VALUE, pts = AV_NOPTS_VALUE, ts, ptsoff; - uint8_t *outbuf; - int hdr, flags, hlen, rlen, outlen; - th_pkt_t *pkt; - AVRational mpeg_tc = {1, 90000}; - - avgstat_add(&st->st_rate, len, dispatch_clock); - - if(LIST_FIRST(&t->tht_muxers) == NULL) - return -1; /* No muxers will take packet, so drop here */ - - if(len < 3) - return -1; - - hdr = getu8(buf, len); - flags = getu8(buf, len); - hlen = getu8(buf, len); - - if(len < hlen || (hdr & 0xc0) != 0x80) - return -1; - - if((flags & 0xc0) == 0xc0) { - if(hlen < 10) - return -1; -#if 0 /* write a more robust DTS/PTS decode which checks for - correct static bits */ - - u8 = getu8(buf, len); - if(u8 >> 4 != 3) { - printf("DTSPTS error\n"); - return -1; - } - - pts = (int64_t)(u8 & 0xe) << 29; - u16 = getu16(buf, len); - if((u16 & 1) == 0) { - printf("DTSPTS error\n"); - return -1; - } -#endif - - - - pts = getpts(buf, len); - dts = getpts(buf, len); - hlen -= 10; - - } else if((flags & 0xc0) == 0x80) { - if(hlen < 5) - return -1; - - dts = pts = getpts(buf, len); - hlen -= 5; - } - - buf += hlen; - len -= hlen; - - if(t->tht_dts_start == AV_NOPTS_VALUE) { - if(dts == AV_NOPTS_VALUE) - return -1; - - t->tht_dts_start = dts; - } - - if(dts != AV_NOPTS_VALUE) { - ptsoff = (int32_t)pts - (int32_t)dts; - dts -= t->tht_dts_start; - dts &= 0x1ffffffffULL; - ts = dts + ((int64_t)st->st_dts_u << 33); - if((ts < 0 || ts > 10000000) && st->st_dts == AV_NOPTS_VALUE) - return -1; - - if(ts < st->st_dts) { - st->st_dts_u++; - ts = dts + ((int64_t)st->st_dts_u << 33); - } - - st->st_dts = dts; - - pts = dts + ptsoff; - - dts = av_rescale_q(dts, mpeg_tc, AV_TIME_BASE_Q); - pts = av_rescale_q(pts, mpeg_tc, AV_TIME_BASE_Q); - } - - while(len > 0) { - rlen = av_parser_parse(st->st_parser, st->st_ctx, - &outbuf, &outlen, buf, len, pts, dts); - - if(outlen) { - pkt = pkt_alloc(outbuf, outlen, st->st_parser->pts, st->st_parser->dts); - pkt->pkt_commercial = t->tht_tt_commercial_advice; - - pes_compute_dts(t, st, pkt); - dts = AV_NOPTS_VALUE; - pts = AV_NOPTS_VALUE; - } - buf += rlen; - len -= rlen; - } - return -1; -} - -/* - * If DTS is unknown we hold packets until we see a packet with correct - * DTS, then we do linear interpolation. - */ -static void -pes_compute_dts(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt) -{ - int64_t dts, d_dts, v; - - if(pkt->pkt_dts == AV_NOPTS_VALUE) { - st->st_dtsq_len++; - pkt->pkt_pts = AV_NOPTS_VALUE; - TAILQ_INSERT_TAIL(&st->st_dtsq, pkt, pkt_queue_link); - return; - } - - if(TAILQ_FIRST(&st->st_dtsq) == NULL) { - st->st_last_dts = pkt->pkt_dts; - pes_compute_pts(t, st, pkt); - return; - } - TAILQ_INSERT_TAIL(&st->st_dtsq, pkt, pkt_queue_link); - v = st->st_dtsq_len + 1; - d_dts = (pkt->pkt_dts - st->st_last_dts) / v; - dts = st->st_last_dts; - - while((pkt = TAILQ_FIRST(&st->st_dtsq)) != NULL) { - dts += d_dts; - pkt->pkt_dts = dts; - pkt->pkt_pts = AV_NOPTS_VALUE; - - TAILQ_REMOVE(&st->st_dtsq, pkt, pkt_queue_link); - pes_compute_pts(t, st, pkt); - } - st->st_dtsq_len = 0; - st->st_last_dts = dts; -} - - -/* - * Compute PTS of packets. - * - * It seems some providers send TV without PTS/DTS for all video - * frames, so we need to reconstruct that here. - * - */ -static void -pes_compute_pts(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt) -{ - th_pkt_t *p; - uint8_t *buf; - uint32_t sc; - - if(pkt->pkt_pts == AV_NOPTS_VALUE) { - - /* PTS not known, figure it out */ - - switch(st->st_type) { - case HTSTV_MPEG2VIDEO: - - /* Figure frame type */ - - if(pkt_len(pkt) < 6) { - pkt_deref(pkt); - return; - } - - buf = pkt_payload(pkt); - sc = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; - - if(sc == 0x100) { /* PICTURE START CODE */ - pkt->pkt_frametype = (buf[5] >> 3) & 7; - } else { - pkt->pkt_frametype = PKT_I_FRAME; - } - - if(pkt->pkt_frametype < PKT_I_FRAME || - pkt->pkt_frametype > PKT_B_FRAME) { - pkt_deref(pkt); - return; - } - - TAILQ_INSERT_TAIL(&st->st_ptsq, pkt, pkt_queue_link); - st->st_ptsq_len++; - - while((pkt = TAILQ_FIRST(&st->st_ptsq)) != NULL) { - switch(pkt->pkt_frametype) { - case PKT_B_FRAME: - /* B-frames have same PTS as DTS, pass them on */ - pkt->pkt_pts = pkt->pkt_dts; - break; - - case PKT_I_FRAME: - case PKT_P_FRAME: - /* Presentation occures at DTS of next I or P frame, - try to find it */ - p = TAILQ_NEXT(pkt, pkt_queue_link); - while(1) { - if(p == NULL) - return; /* not arrived yet, wait */ - if(p->pkt_frametype <= PKT_P_FRAME) { - pkt->pkt_pts = p->pkt_dts; - break; - } - p = TAILQ_NEXT(p, pkt_queue_link); - } - break; - } - - TAILQ_REMOVE(&st->st_ptsq, pkt, pkt_queue_link); - st->st_ptsq_len--; - pes_compute_duration(t, st, pkt); - } - return; - - case HTSTV_H264: - /* For h264, we cannot do anything (yet) */ - pkt->pkt_pts = pkt->pkt_dts; /* this is wrong */ - break; - - default: - /* Rest is audio, no decoder delay */ - pkt->pkt_pts = pkt->pkt_dts; - break; - } - } - pes_compute_duration(t, st, pkt); -} - - -/* - * Compute duration of a packet, we do this by keeping a packet - * until the next one arrives, then we release it - */ -void -pes_compute_duration(th_transport_t *t, th_stream_t *st, th_pkt_t *pkt) -{ - th_pkt_t *next; - th_muxer_t *tm; - int delta; - int64_t d; - delta = abs(pkt->pkt_dts - pkt->pkt_pts); - - if(delta > 250000) - delta = 250000; - - if(delta > st->st_peak_presentation_delay) - st->st_peak_presentation_delay = delta; - - TAILQ_INSERT_TAIL(&st->st_durationq, pkt, pkt_queue_link); - - pkt = TAILQ_FIRST(&st->st_durationq); - if((next = TAILQ_NEXT(pkt, pkt_queue_link)) == NULL) - return; - - d = next->pkt_dts - pkt->pkt_dts; - - TAILQ_REMOVE(&st->st_durationq, pkt, pkt_queue_link); - - if(d != st->st_last_duration) { - - if(st->st_last_duration_frames > 30) { - syslog(LOG_INFO, "\"%s\" on \"%s\": \"%s\" " - "Non-linear timestamps detected. " - "DTS delta was %lld, but should be %lld for this stream", - t->tht_channel->ch_name, - t->tht_name, - htstvstreamtype2txt(st->st_type), - d, st->st_last_duration); - } - st->st_last_duration = d; - st->st_last_duration_frames = 0; - } else { - st->st_last_duration_frames++; - } - - if(d < 1 || d > 500000) { - pkt_deref(pkt); - return; - } - - pkt->pkt_duration = d; - pkt->pkt_stream = st; - - /* Alert all muxers tied to us that a new packet has arrived */ - - LIST_FOREACH(tm, &t->tht_muxers, tm_transport_link) - tm->tm_new_pkt(tm, st, pkt); - - /* Unref (and possibly free) the packet, muxers are supposed - to increase refcount or copy packet if they need anything */ - - pkt_deref(pkt); -} diff --git a/psi.c b/psi.c index 0be811c3..8e359305 100644 --- a/psi.c +++ b/psi.c @@ -275,6 +275,8 @@ psi_parse_pmt(th_transport_t *t, uint8_t *ptr, int len, int chksvcid) if(hts_stream_type != 0) { st = transport_add_stream(t, pid, hts_stream_type); + st->st_tb = (AVRational){1, 90000}; + memcpy(st->st_lang, lang, 4); } } @@ -326,6 +328,9 @@ psi_build_pmt(th_muxer_t *tm, uint8_t *buf0, int maxlen, int pcrpid) LIST_FOREACH(tms, &tm->tm_streams, tms_muxer_link0) { st = tms->tms_stream; + if(tms->tms_index == 0) + continue; + switch(st->st_type) { case HTSTV_MPEG2VIDEO: c = 0x02; diff --git a/transports.c b/transports.c index 816dfe78..48933781 100644 --- a/transports.c +++ b/transports.c @@ -50,7 +50,6 @@ #include "dvb_dvr.h" #include "iptv_input.h" #include "psi.h" -#include "pes.h" #include "buffer.h" #include "channels.h" #include "cwc.h" @@ -107,14 +106,8 @@ transport_stop(th_transport_t *t, int flush_subscriptions) st->st_buffer_ptr = 0; st->st_startcode = 0; - /* Clear DTS queue */ - - while((pkt = TAILQ_FIRST(&st->st_dtsq)) != NULL) { - TAILQ_REMOVE(&st->st_dtsq, pkt, pkt_queue_link); - assert(pkt->pkt_refcount == 1); - pkt_deref(pkt); - } - st->st_dtsq_len = 0; + if(st->st_curpkt != NULL) + pkt_deref(st->st_curpkt); /* Clear PTS queue */ @@ -163,10 +156,12 @@ transport_start(th_transport_t *t, unsigned int weight) LIST_FOREACH(st, &t->tht_streams, st_link) { st->st_startcond = 0xffffffff; - - st->st_dts = AV_NOPTS_VALUE; - st->st_last_dts = 0; - st->st_dts_u = 0; + st->st_curdts = AV_NOPTS_VALUE; + st->st_curpts = AV_NOPTS_VALUE; + st->st_prevdts = AV_NOPTS_VALUE; + + st->st_last_dts = AV_NOPTS_VALUE; + st->st_dts_epoch = 0; /* Open ffmpeg context and parser */ @@ -373,7 +368,6 @@ transport_add_stream(th_transport_t *t, int pid, tv_streamtype_t type) st->st_demuxer_fd = -1; LIST_INSERT_HEAD(&t->tht_streams, st, st_link); - TAILQ_INIT(&st->st_dtsq); TAILQ_INIT(&st->st_ptsq); TAILQ_INIT(&st->st_durationq); TAILQ_INIT(&st->st_pktq); diff --git a/tsdemux.c b/tsdemux.c index 672f50e8..5a4f04fc 100644 --- a/tsdemux.c +++ b/tsdemux.c @@ -44,10 +44,10 @@ #include "teletext.h" #include "transports.h" #include "subscriptions.h" -#include "pes.h" #include "psi.h" #include "buffer.h" #include "tsdemux.h" +#include "parsers.h" /** @@ -161,7 +161,7 @@ ts_recv_packet(th_transport_t *t, int pid, uint8_t *tsb, int dodescramble) if(afl > 188) break; - pes_reassembly(t, st, tsb + afl, 188 - afl, pusi, err); + parse_raw_mpeg(t, st, tsb + afl, 188 - afl, pusi, err); break; } } diff --git a/tsmux.c b/tsmux.c index 41c79329..1780e957 100644 --- a/tsmux.c +++ b/tsmux.c @@ -1,6 +1,6 @@ /* * tvheadend, MPEG Transport stream muxer - * Copyright (C) 2007 Andreas Öman + * Copyright (C) 2008 Andreas Öman * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,15 +39,15 @@ #include "teletext.h" #include "transports.h" #include "subscriptions.h" -#include "pes.h" #include "psi.h" #include "buffer.h" #include "mux.h" #include "tsmux.h" -#define TS_LOOKAHEAD 500000 +static void lookahead_dequeue(ts_muxer_t *ts, th_muxstream_t *tms); static const AVRational mpeg_tc = {1, 90000}; +static const AVRational mpeg_tc_27M = {1, 27000000}; #define PID_PMT 1000 #define PID_ES_BASE 2000 @@ -58,9 +58,36 @@ static const AVRational mpeg_tc = {1, 90000}; static void ts_muxer_send_packet(ts_muxer_t *ts) { + int i; + int64_t t, tlow, pcr; + uint8_t *d; + if(ts->ts_block == 0) return; + d = ts->ts_packet; + + /* Update PCR */ + + if(ts->ts_pcr_ref != AV_NOPTS_VALUE) { + for(i = 0; i < ts->ts_block; i++) { + d = ts->ts_packet + i * 188; + if((d[3] & 0xf0) == 0x30 && d[4] >= 7 && d[5] & 0x10) { + + pcr = getclock_hires() - ts->ts_pcr_ref; + t = av_rescale_q(pcr, AV_TIME_BASE_Q, mpeg_tc_27M); + tlow = t % 300LL; + t = t / 300LL; + + d[6] = t >> 25; + d[7] = t >> 17; + d[8] = t >> 9; + d[9] = t >> 1; + d[10] = ((t & 1) << 7) | ((tlow >> 8) & 1); + d[11] = tlow; + } + } + } ts->ts_output(ts->ts_output_opaque, ts->ts_subscription, ts->ts_packet, ts->ts_block, 0); ts->ts_block = 0; @@ -154,365 +181,379 @@ ts_muxer_generate_tables(void *aux, int64_t now) ts_muxer_build_table(ts, table, l, ts->ts_pat_cc, 0); ts->ts_pat_cc++; - l = psi_build_pmt(tm, table, sizeof(table), ts->ts_pcrpid); + l = psi_build_pmt(tm, table, sizeof(table), ts->ts_pcr_stream->tms_index); ts_muxer_build_table(ts, table, l, ts->ts_pmt_cc, PID_PMT); ts->ts_pmt_cc++; ts_muxer_send_packet(ts); } + + + /** * */ -static int64_t -get_delay(th_muxstream_t *tms) +static void +tmf_enq(th_muxfifo_t *tmf, th_muxpkt_t *tm) { - th_metapkt_t *f, *l; + /* record real content size */ + tmf->tmf_contentsize += tm->tm_contentsize; - f = TAILQ_FIRST(&tms->tms_metaqueue); - if(f == NULL) - return -1; - - l = TAILQ_LAST(&tms->tms_metaqueue, th_metapkt_queue); - - return l->tm_ts_stop - f->tm_ts_start; /* Delta time */ + /* Enqueue packet */ + TAILQ_INSERT_TAIL(&tmf->tmf_queue, tm, tm_link); + tmf->tmf_len++; +} +/** + * + */ +static void +tmf_remove(th_muxfifo_t *tmf, th_muxpkt_t *tm) +{ + tmf->tmf_contentsize -= tm->tm_contentsize; + TAILQ_REMOVE(&tmf->tmf_queue, tm, tm_link); + tmf->tmf_len--; } /** * */ -#if 0 -static int -estimate_bitrate(th_muxstream_t *tms) +static th_muxpkt_t * +tmf_deq(th_muxfifo_t *tmf) { - int64_t delta, rate; + th_muxpkt_t *tm; - delta = get_delay(tms); - if(delta == -1) - return -1; - rate = (uint64_t)tms->tms_meta_bytes * 1000000LL / delta; - return rate; + tm = TAILQ_FIRST(&tmf->tmf_queue); + if(tm != NULL) + tmf_remove(tmf, tm); + return tm; } -#endif + + + /** * */ -#if 0 -static int -estimate_blockrate(th_muxstream_t *tms) +static void +tmf_init(th_muxfifo_t *tmf) { - int64_t delta, rate; - - delta = get_delay(tms); - if(delta == -1) - return 0; - rate = (uint64_t)tms->tms_meta_packets * 1000000LL / delta; - return rate; + TAILQ_INIT(&tmf->tmf_queue); } -#endif + /** * */ -#define PES_HEADER_SIZE 19 +static void +ts_deliver(void *opaque, int64_t now) +{ + th_muxstream_t *tms = opaque; + th_muxer_t *tm = tms->tms_muxer; + ts_muxer_t *ts = tm->tm_opaque; + th_muxpkt_t *f; + th_muxfifo_t *tmf = &tms->tms_delivery_fifo; + int64_t pcr = now - ts->ts_pcr_ref; + int64_t dl, next, delta; + + f = tmf_deq(tmf); + dl = f->tm_deadline; + + delta = pcr - dl; + + ts_muxer_add_packet(ts, f->tm_pkt, tms->tms_index); + free(f); + + f = TAILQ_FIRST(&tmf->tmf_queue); /* next packet we are going to send */ + if(f == NULL) { + lookahead_dequeue(ts, tms); + f = TAILQ_FIRST(&tmf->tmf_queue); + if(f == NULL) + return; + } + + next = f->tm_deadline + ts->ts_pcr_ref; + if(next < now + 100) + next = now + 100; + + dtimer_arm_hires(&tms->tms_mux_timer, ts_deliver, tms, next - 500); +} + + + +/** + * Check if we need to start delivery timer for the given stream + * + * Also, if it is the PCR stream and we're not yet runnig, figure out + * PCR and start generating packets + */ +static void +ts_check_deliver(ts_muxer_t *ts, th_muxstream_t *tms) +{ + int64_t now; + th_muxpkt_t *f; + th_muxfifo_t *tmf = &tms->tms_delivery_fifo; + int64_t next; + + if(dtimer_isarmed(&tms->tms_mux_timer)) + return; /* timer already running, we're fine */ + + assert(tms->tms_delivery_fifo.tmf_len != 0); + + now = getclock_hires(); + + if(ts->ts_pcr_ref == AV_NOPTS_VALUE) { + + if(ts->ts_pcr_start == AV_NOPTS_VALUE) + return; /* dont know anything yet */ + + ts->ts_pcr_ref = now - ts->ts_pcr_start; + } + + f = TAILQ_FIRST(&tmf->tmf_queue); /* next packet we are going to send */ + next = f->tm_deadline + ts->ts_pcr_ref; + + if(next < now + 100) + next = now + 100; + + dtimer_arm_hires(&tms->tms_mux_timer, ts_deliver, tms, next - 500); +} + + + + +/** + * Generate TS packet, see comments inline + * + * TODO: Dont insert both DTS and PTS if they're equal + */ static void -ts_mux_gen_packets(ts_muxer_t *ts, th_muxstream_t *tms, th_pkt_t *pkt) +lookahead_dequeue(ts_muxer_t *ts, th_muxstream_t *tms) { - th_metapkt_t *tm; - int off = 0; + // th_stream_t *st = tms->tms_stream; + th_pkt_t *pkt; + th_muxpkt_t *tm; + th_refpkt_t *o; uint8_t *tsb; - int64_t t; - int frrem, pad, tsrem, len; uint16_t u16; - // int pcroffset; - int printts = 0; + int frrem, pad, tsrem, len, off, cc, hlen, flags; + int64_t t, tdur, toff, tlen, dts, pcr, basedelivery; - if(printts)printf("Generating TS packets, DTS = %lld +%d\n", pkt->pkt_dts, - pkt->pkt_duration); + tlen = 0; + TAILQ_FOREACH(o, &tms->tms_lookahead, trp_link) { + pkt = o->trp_pkt; + tlen += pkt->pkt_payloadlen; - while(off < pkt->pkt_payloadlen) { - - - tm = malloc(sizeof(th_metapkt_t) + 188); - tsb = tm->tm_pkt; - tm->tm_pcroffset = 0; - - /* Timestamp of first byte */ - tm->tm_ts_start = pkt->pkt_duration * off / - (pkt->pkt_payloadlen + PES_HEADER_SIZE) - + pkt->pkt_dts - tms->tms_muxoffset; - - - if(ts->ts_flags & TS_HTSCLIENT) { - /* Temporary hack */ - *tsb++ = tms->tms_stream->st_type; + if(pkt->pkt_pts != pkt->pkt_dts) { + tlen += 19; /* pes header with DTS and PTS */ } else { - /* TS marker */ - *tsb++ = 0x47; + tlen += 14; /* pes header with PTS only */ + } + } + + if(tlen == 0) + return; + + o = TAILQ_FIRST(&tms->tms_lookahead); + pkt = o->trp_pkt; + toff = 0; + + /* XXX: assumes duration is linear, but it's probably ok */ + tdur = pkt->pkt_duration * tms->tms_lookahead_packets; + + if(tms->tms_mux_offset == AV_NOPTS_VALUE) { + if(tms->tms_stream->st_vbv_delay == -1) + tms->tms_mux_offset = tdur + pkt->pkt_duration; + else + tms->tms_mux_offset = tms->tms_stream->st_vbv_delay; + } + + if(ts->ts_pcr_start == AV_NOPTS_VALUE && ts->ts_pcr_stream == tms) + ts->ts_pcr_start = pkt->pkt_dts - tms->tms_mux_offset; + + basedelivery = pkt->pkt_dts - tms->tms_mux_offset; + + while((o = TAILQ_FIRST(&tms->tms_lookahead)) != NULL) { + + off = 0; + pkt = o->trp_pkt; + + pkt_load(pkt); + + if(pkt->pkt_dts == pkt->pkt_pts) { + hlen = 8; + flags = 0x80; + } else { + hlen = 13; + flags = 0xc0; } + while(off < pkt->pkt_payloadlen) { + + tm = malloc(sizeof(th_muxpkt_t) + 188); + tm->tm_deadline = basedelivery + tdur * toff / tlen; + + dts = (int64_t)pkt->pkt_duration * + (int64_t)off / (int64_t)pkt->pkt_payloadlen; + dts += pkt->pkt_dts; + + tm->tm_dts = dts; + tsb = tm->tm_pkt; + + if(ts->ts_flags & TS_HTSCLIENT) { + /* Temporary hack */ + *tsb++ = tms->tms_stream->st_type; + } else { + /* TS marker */ + *tsb++ = 0x47; + } + - /* Write PID and optionally payload unit start indicator */ - *tsb++ = tms->tms_index >> 8 | (off ? 0 : 0x40); - *tsb++ = tms->tms_index; + /* Write PID and optionally payload unit start indicator */ + *tsb++ = tms->tms_index >> 8 | (off ? 0 : 0x40); + *tsb++ = tms->tms_index; - /* Remaing bytes after 4 bytes of TS header */ - tsrem = 184; + cc = tms->tms_cc & 0xf; + tms->tms_cc++; - if(off == 0) { - /* When writing the packet header, shave of a bit of available - payload size */ - tsrem -= PES_HEADER_SIZE; - } + /* Remaing bytes after 4 bytes of TS header */ + tsrem = 184; + + if(off == 0) { + /* When writing the packet header, shave of a bit of available + payload size */ + tsrem -= hlen + 6; + } - /* Remaining length of frame */ - frrem = pkt->pkt_payloadlen - off; + /* Remaining length of frame */ + frrem = pkt->pkt_payloadlen - off; - /* Compute amout of padding needed */ - pad = tsrem - frrem; + /* Compute amout of padding needed */ + pad = tsrem - frrem; - if(pad > 0) { - /* Must pad TS packet */ + pcr = tm->tm_deadline; + tm->tm_pcr = AV_NOPTS_VALUE; + + if(ts->ts_pcr_stream == tms && ts->ts_pcr_last + 20000 < pcr) { + + tm->tm_pcr = pcr; + /* Insert PCR */ + + tlen += 8; /* compensate total length */ + tsrem -= 8; + pad -= 8; + if(pad < 0) + pad = 0; - *tsb++ = 0x30; - tsrem -= pad; - *tsb++ = --pad; + *tsb++ = 0x30 | cc; + *tsb++ = 7 + pad; + *tsb++ = 0x10; /* PCR flag */ + + t = av_rescale_q(pcr, AV_TIME_BASE_Q, mpeg_tc); + *tsb++ = t >> 25; + *tsb++ = t >> 17; + *tsb++ = t >> 9; + *tsb++ = t >> 1; + *tsb++ = (t & 1) << 7; + *tsb++ = 0; + + memset(tsb, 0xff, pad); + tsb += pad; + tsrem -= pad; + + ts->ts_pcr_last = pcr + 20000; - memset(tsb, 0x00, pad); - tsb += pad; - } else { - *tsb++ = 0x10; - } + } else if(pad > 0) { + /* Must pad TS packet */ + + *tsb++ = 0x30 | cc; + tsrem -= pad; + *tsb++ = --pad; - - if(off == 0) { - /* Insert PES header */ - - /* Write startcode */ - - *tsb++ = 0; - *tsb++ = 0; - *tsb++ = tms->tms_sc >> 8; - *tsb++ = tms->tms_sc; - - /* Write total frame length (without accounting for startcode and - length field itself */ - - len = pkt_len(pkt) + PES_HEADER_SIZE - 6; - - if(len > 65535) { - /* It's okay to write len as 0 in transport streams, - but only for video frames, and i dont expect any of the - audio frames to exceed 64k - */ - len = 0; + memset(tsb, 0x00, pad); + tsb += pad; + } else { + *tsb++ = 0x10 | cc; } - *tsb++ = len >> 8; - *tsb++ = len; - *tsb++ = 0x80; /* MPEG2 */ - *tsb++ = 0xc0; /* pts & dts is present */ - *tsb++ = 10; /* length of rest of header (pts & dts) */ - - /* Write PTS */ - - t = av_rescale_q(pkt->pkt_pts, AV_TIME_BASE_Q, mpeg_tc); - *tsb++ = (((t >> 30) & 7) << 1) | 1; - u16 = (((t >> 15) & 0x7fff) << 1) | 1; - *tsb++ = u16 >> 8; - *tsb++ = u16; - u16 = ((t & 0x7fff) << 1) | 1; - *tsb++ = u16 >> 8; - *tsb++ = u16; - - /* Write DTS */ - - t = av_rescale_q(pkt->pkt_dts, AV_TIME_BASE_Q, mpeg_tc); - *tsb++ = (((t >> 30) & 7) << 1) | 1; - u16 = (((t >> 15) & 0x7fff) << 1) | 1; - *tsb++ = u16 >> 8; - *tsb++ = u16; - u16 = ((t & 0x7fff) << 1) | 1; - *tsb++ = u16 >> 8; - *tsb++ = u16; - } - - memcpy(tsb, pkt->pkt_payload + off, tsrem); - - /* Timestamp of last byte + 1 */ - t = pkt->pkt_duration * (off + tsrem) / (pkt->pkt_payloadlen + - PES_HEADER_SIZE); - - /* Fix any rounding errors */ - if(t > pkt->pkt_duration) - t = pkt->pkt_duration; - - tm->tm_ts_stop = pkt->pkt_dts + t - tms->tms_muxoffset; - - - if(printts)printf("TS: copy %7d (%3d bytes) pad = %7d: %lld - %lld\n", - off, tsrem, pad, tm->tm_ts_start, tm->tm_ts_stop); - - TAILQ_INSERT_TAIL(&tms->tms_metaqueue, tm, tm_link); - tms->tms_meta_packets++; - - off += tsrem; - } - if(printts)printf("end @ %lld\n", pkt->pkt_dts + pkt->pkt_duration); - if(printts)exit(0); -} - - -/** - * - */ -static int64_t -check_total_delay(th_muxer_t *tm) -{ - th_muxstream_t *tms; - int64_t delta = -1, v; - LIST_FOREACH(tms, &tm->tm_streams, tms_muxer_link0) { - v = get_delay(tms); - if(v > delta) - delta = v; - } - return delta; -} - - - -/** - * - */ -static int64_t -get_start_pcr(th_muxer_t *tm) -{ - th_muxstream_t *tms; - th_metapkt_t *f; - int64_t r = INT64_MAX; - - LIST_FOREACH(tms, &tm->tm_streams, tms_muxer_link0) { - - f = TAILQ_FIRST(&tms->tms_metaqueue); - if(f == NULL) - continue; - if(f->tm_ts_start < r) - r = f->tm_ts_start; - } - return r; -} - - - - -/** - * - */ -static void -ts_deliver(void *aux, int64_t now) -{ - ts_muxer_t *ts = aux; - th_muxer_t *tm = ts->ts_muxer; - th_muxstream_t *tms, *c; - int rate; - int64_t v, pcr; - th_metapkt_t *x, *y; - uint8_t *tsb; - - int cnt; - - pcr = now - ts->ts_pcr_ref + ts->ts_pcr_offset; - - if(ts->ts_last_pcr + 20000 < now) { - LIST_FOREACH(tms, &tm->tm_streams, tms_muxer_link0) - if(tms->tms_index == ts->ts_pcrpid) - break; - - if(tms != NULL) { - uint8_t pkt[188]; - tsb = &pkt[0]; - *tsb++ = 0x47; - *tsb++ = 0; - *tsb++ = 0; - - /* Insert CC */ - *tsb++ = 0x20 | (tms->tms_cc & 0xf); - - *tsb++ = 183; - *tsb++ = 0x10; /* PCR flag */ - - v = av_rescale_q(pcr, AV_TIME_BASE_Q, mpeg_tc); - *tsb++ = v >> 25; - *tsb++ = v >> 17; - *tsb++ = v >> 9; - *tsb++ = v >> 1; - *tsb++ = (v & 1) << 7; - *tsb++ = 0; - - ts_muxer_add_packet(ts, pkt, tms->tms_index); - ts->ts_last_pcr = now; - } - } - - cnt = ts->ts_blocks_per_packet - ts->ts_block; - - while(--cnt >= 0) { - c = LIST_FIRST(&tm->tm_streams); - tms = LIST_NEXT(c, tms_muxer_link0); - for(; tms != NULL; tms = LIST_NEXT(tms, tms_muxer_link0)) { - x = TAILQ_FIRST(&c->tms_metaqueue); - y = TAILQ_FIRST(&tms->tms_metaqueue); + if(off == 0) { + /* Insert PES header */ - if(x != NULL && y != NULL && y->tm_ts_start < x->tm_ts_start) - c = tms; - } + /* Write startcode */ + *tsb++ = 0; + *tsb++ = 0; + *tsb++ = tms->tms_sc >> 8; + *tsb++ = tms->tms_sc; - tms = NULL; + /* Write total frame length (without accounting for startcode and + length field itself */ + len = pkt_len(pkt) + hlen; - x = TAILQ_FIRST(&c->tms_metaqueue); - if(x == NULL) { - printf("underrun\n"); - break; - } - TAILQ_REMOVE(&c->tms_metaqueue, x, tm_link); - c->tms_meta_packets--; + if(tms->tms_stream->st_type == HTSTV_MPEG2VIDEO) { + /* It's okay to write len as 0 in transport streams, + but only for video frames, and i dont expect any of the + audio frames to exceed 64k + */ + len = 0; + } - /* Insert CC */ - x->tm_pkt[3] = (x->tm_pkt[3] & 0xf0) | (c->tms_cc & 0xf); - c->tms_cc++; + *tsb++ = len >> 8; + *tsb++ = len; - ts_muxer_add_packet(ts, x->tm_pkt, c->tms_index); - free(x); - } + *tsb++ = 0x80; /* MPEG2 */ + *tsb++ = flags; + *tsb++ = hlen - 3; /* length of rest of header (pts & dts) */ + /* Write PTS */ + + if(flags == 0xc0) { + t = av_rescale_q(pkt->pkt_pts, AV_TIME_BASE_Q, mpeg_tc); + *tsb++ = (((t >> 30) & 7) << 1) | 1; + u16 = (((t >> 15) & 0x7fff) << 1) | 1; + *tsb++ = u16 >> 8; + *tsb++ = u16; + u16 = ((t & 0x7fff) << 1) | 1; + *tsb++ = u16 >> 8; + *tsb++ = u16; + } - rate = 0; - LIST_FOREACH(tms, &tm->tm_streams, tms_muxer_link0) { - rate += (tms->tms_meta_packets * 1000000ULL) / (uint64_t)TS_LOOKAHEAD; - // rate += estimate_blockrate(tms); - } + /* Write DTS */ -#if 0 - printf("TS blockrate = %d\n", rate); - - LIST_FOREACH(tms, &tm->tm_streams, tms_muxer_link0) { - printf("%-10s: %-5d %-8lld | ", - htstvstreamtype2txt(tms->tms_stream->st_type), - tms->tms_meta_packets, - get_delay(tms)); - } - printf("\n"); -#endif + t = av_rescale_q(pkt->pkt_dts, AV_TIME_BASE_Q, mpeg_tc); + *tsb++ = (((t >> 30) & 7) << 1) | 1; + u16 = (((t >> 15) & 0x7fff) << 1) | 1; + *tsb++ = u16 >> 8; + *tsb++ = u16; + u16 = ((t & 0x7fff) << 1) | 1; + *tsb++ = u16 >> 8; + *tsb++ = u16; + } - v = 1000000 / (rate / ts->ts_blocks_per_packet); - dtimer_arm_hires(&ts->ts_stream_timer, ts_deliver, ts, now + v); + memcpy(tsb, pkt->pkt_payload + off, tsrem); + tm->tm_contentsize = tsrem; + + tmf_enq(&tms->tms_delivery_fifo, tm); + + toff += tsrem; + off += tsrem; + } + + tms->tms_lookahead_depth -= pkt->pkt_payloadlen; + tms->tms_lookahead_packets--; + pkt_deref(pkt); + TAILQ_REMOVE(&tms->tms_lookahead, o, trp_link); + + free(o); + } + ts_check_deliver(ts, tms); } + + /** * */ @@ -520,27 +561,27 @@ static void ts_mux_packet_input(void *opaque, th_muxstream_t *tms, th_pkt_t *pkt) { ts_muxer_t *ts = opaque; - th_muxer_t *tm = ts->ts_muxer; - int64_t v, pcr; + th_stream_t *st = tms->tms_stream; + th_refpkt_t *trp; - ts_mux_gen_packets(ts, tms, pkt); + if(tms->tms_index == 0) + return; - if(ts->ts_running == 0) { - v = check_total_delay(tm); - if(v < TS_LOOKAHEAD) - return; - pcr = get_start_pcr(tm); - if(pcr == INT64_MAX) - return; - ts->ts_pcr_offset = pcr; - ts->ts_pcr_ref = getclock_hires(); - ts->ts_running = 1; - ts_deliver(ts, getclock_hires()); + if(st->st_vbv_delay == -1) { + if(tms->tms_lookahead_depth + pkt->pkt_payloadlen > st->st_vbv_size) + lookahead_dequeue(ts, tms); + } else { + lookahead_dequeue(ts, tms); } + + trp = malloc(sizeof(th_refpkt_t)); + trp->trp_pkt = pkt_ref(pkt); + tms->tms_lookahead_depth += pkt->pkt_payloadlen; + tms->tms_lookahead_packets++; + TAILQ_INSERT_TAIL(&tms->tms_lookahead, trp, trp_link); + } - - /** * */ @@ -555,8 +596,6 @@ ts_muxer_init(th_subscription_t *s, ts_mux_output_t *output, th_muxer_t *tm; th_stream_t *st; - ts->ts_pcr_offset = AV_NOPTS_VALUE; - ts->ts_pcr_ref = AV_NOPTS_VALUE; ts->ts_output = output; ts->ts_output_opaque = opaque; @@ -567,6 +606,10 @@ ts_muxer_init(th_subscription_t *s, ts_mux_output_t *output, ts->ts_blocks_per_packet = 7; ts->ts_packet = malloc(188 * ts->ts_blocks_per_packet); + + ts->ts_pcr_start = AV_NOPTS_VALUE; + ts->ts_pcr_ref = AV_NOPTS_VALUE; + ts->ts_pcr_last = INT64_MIN; /* Do TS MUX specific init per stream */ @@ -576,29 +619,34 @@ ts_muxer_init(th_subscription_t *s, ts_mux_output_t *output, dopcr = 0; switch(st->st_type) { case HTSTV_MPEG2VIDEO: - tms->tms_muxoffset = 200000; - tms->tms_sc = 0x1ec; + tms->tms_sc = 0x1e0; dopcr = 1; break; case HTSTV_MPEG2AUDIO: - tms->tms_muxoffset = 75000; - tms->tms_sc = 0x1cd; + tms->tms_sc = 0x1c0; + st->st_vbv_delay = 45000; break; +#if 0 case HTSTV_AC3: - tms->tms_muxoffset = 75000; tms->tms_sc = 0x1bd; + st->st_vbv_delay = 50000; break; case HTSTV_H264: tms->tms_muxoffset = 900000; tms->tms_sc = 0x1e0; dopcr = 1; break; +#endif default: continue; } - if(dopcr && ts->ts_pcrpid == 0) - ts->ts_pcrpid = pididx; + tms->tms_mux_offset = AV_NOPTS_VALUE; + tmf_init(&tms->tms_delivery_fifo); + TAILQ_INIT(&tms->tms_lookahead); + + if(dopcr && ts->ts_pcr_stream == NULL) + ts->ts_pcr_stream = tms; tms->tms_index = pididx++; } diff --git a/tsmux.h b/tsmux.h index b3b5daf2..6d31f1b1 100644 --- a/tsmux.h +++ b/tsmux.h @@ -20,8 +20,7 @@ #define TSMUX_H typedef void (ts_mux_output_t)(void *opaque, th_subscription_t *s, - uint8_t *pkt, int npackets, int64_t pcr); - + uint8_t *pkt, int npackets, int64_t pcr_ref); typedef struct ts_muxer { th_subscription_t *ts_subscription; @@ -29,30 +28,23 @@ typedef struct ts_muxer { #define TS_SEEK 0x1 #define TS_HTSCLIENT 0x2 - int ts_running; - th_muxer_t *ts_muxer; ts_mux_output_t *ts_output; void *ts_output_opaque; - int64_t ts_pcr_offset; - int64_t ts_pcr_ref; - - int ts_pat_cc; int ts_pmt_cc; - dtimer_t ts_patpmt_timer; uint8_t *ts_packet; int ts_block; int ts_blocks_per_packet; - dtimer_t ts_stream_timer; + th_muxstream_t *ts_pcr_stream; - int ts_pcrpid; - - int64_t ts_last_pcr; + int64_t ts_pcr_start; + int64_t ts_pcr_ref; /* System clock when PCR was/is/will be 0 */ + int64_t ts_pcr_last; } ts_muxer_t; diff --git a/tvhead.h b/tvhead.h index 29793130..c388df0e 100644 --- a/tvhead.h +++ b/tvhead.h @@ -27,6 +27,7 @@ #include #include #include "refstr.h" +#include /* * Commercial status @@ -86,7 +87,8 @@ LIST_HEAD(th_pkt_list, th_pkt); LIST_HEAD(th_muxer_list, th_muxer); LIST_HEAD(th_muxstream_list, th_muxstream); LIST_HEAD(th_descrambler_list, th_descrambler); -TAILQ_HEAD(th_metapkt_queue, th_metapkt); +TAILQ_HEAD(th_refpkt_queue, th_refpkt); +TAILQ_HEAD(th_muxpkt_queue, th_muxpkt); extern time_t dispatch_clock; extern int startupcounter; @@ -295,11 +297,19 @@ typedef struct th_stream { int st_buffer_errors; /* Errors accumulated for this packet */ uint32_t st_startcond; uint32_t st_startcode; + uint32_t st_startcode_offset; + int st_parser_state; + + struct th_pkt *st_curpkt; + int64_t st_curpts; + int64_t st_curdts; + int64_t st_prevdts; + int st_frame_duration; /* DTS generator */ - int32_t st_dts_u; /* upper bits (auto generated) */ - int64_t st_dts; + int64_t st_dts_epoch; /* upper bits (auto generated) */ + int64_t st_last_dts; /* Codec */ @@ -310,12 +320,6 @@ typedef struct th_stream { struct th_pkt_list st_packets; - /* Temporary frame store for calculating DTS */ - - struct th_pkt_queue st_dtsq; - int st_dtsq_len; - int64_t st_last_dts; - /* Temporary frame store for calculating PTS */ struct th_pkt_queue st_ptsq; @@ -325,9 +329,6 @@ typedef struct th_stream { struct th_pkt_queue st_durationq; - int64_t st_last_duration; - int st_last_duration_frames; - /* Final frame store */ struct th_pkt_queue st_pktq; @@ -338,6 +339,12 @@ typedef struct th_stream { char st_lang[4]; /* ISO 639 3-letter language code */ + /* Remuxing information */ + AVRational st_tb; + + int st_vbv_size; /* Video buffer size (in bytes) */ + int st_vbv_delay; /* -1 if CBR */ + } th_stream_t; @@ -523,23 +530,51 @@ typedef struct th_pkt { TAILQ_ENTRY(th_pkt) pkt_mem_link; } th_pkt_t; +/** + * Referenced packets + */ +typedef struct th_refpkt { + TAILQ_ENTRY(th_refpkt) trp_link; + th_pkt_t *trp_pkt; +} th_refpkt_t; /** - * Meta packets + * Muxed packets */ -typedef struct th_metapkt { - TAILQ_ENTRY(th_metapkt) tm_link; - int64_t tm_ts_start; - int64_t tm_ts_stop; - int tm_pcroffset; +typedef struct th_muxpkt { + TAILQ_ENTRY(th_muxpkt) tm_link; + int64_t tm_pcr; + int64_t tm_dts; + + int tm_contentsize; + int64_t tm_deadline; /* Packet transmission deadline */ + uint8_t tm_pkt[0]; -} th_metapkt_t; +} th_muxpkt_t; /* * A mux stream reader */ + + +struct th_subscription; +struct th_muxstream; + +typedef void (th_mux_output_t)(void *opaque, struct th_muxstream *tms, + th_pkt_t *pkt); + + +typedef struct th_muxfifo { + struct th_muxpkt_queue tmf_queue; + uint32_t tmf_len; + int tmf_contentsize; + +} th_muxfifo_t; + + + typedef struct th_muxstream { LIST_ENTRY(th_muxstream) tms_muxer_link0; @@ -547,17 +582,26 @@ typedef struct th_muxstream { th_stream_t *tms_stream; int tms_index; /* Used as PID or whatever */ - struct th_metapkt_queue tms_metaqueue; - uint32_t tms_meta_packets; /* number of packets "" */ + struct th_refpkt_queue tms_lookahead; - + int tms_lookahead_depth; /* bytes in lookahead queue */ + int tms_lookahead_packets; + + th_muxfifo_t tms_cbr_fifo; + th_muxfifo_t tms_delivery_fifo; + + int64_t tms_deadline; + int64_t tms_delay; + int64_t tms_delta; + int64_t tms_mux_offset; + + dtimer_t tms_mux_timer; /* MPEG TS multiplex stuff */ int tms_sc; /* start code */ int tms_cc; - int64_t tms_muxoffset; /* DTS offset from PCR */ - + /* Memebers used when running with ffmpeg */ struct AVStream *tms_avstream; @@ -566,7 +610,6 @@ typedef struct th_muxstream { int tms_blockcnt; int64_t tms_dl; int64_t tms_staletime; - } th_muxstream_t; @@ -574,11 +617,6 @@ typedef struct th_muxstream { * */ -struct th_subscription; - -typedef void (th_mux_output_t)(void *opaque, th_muxstream_t *tms, - th_pkt_t *pkt); - typedef void (th_mux_newpkt_t)(struct th_muxer *tm, th_stream_t *st, th_pkt_t *pkt); diff --git a/v4l.c b/v4l.c index b28e34bd..317de825 100644 --- a/v4l.c +++ b/v4l.c @@ -43,7 +43,6 @@ #include "channels.h" #include "dispatch.h" #include "transports.h" -#include "pes.h" struct th_v4l_adapter_list v4l_adapters; @@ -337,7 +336,7 @@ v4l_fd_callback(int events, void *opaque, int fd) st->st_buffer_ptr += r; if(st->st_buffer_ptr == l) { - pes_packet_input(t, st, pkt, l); + // pes_packet_input(t, st, pkt, l); st->st_buffer_size = 0; tva->tva_startcode = 0; }