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
This commit is contained in:
Andreas Öman 2008-02-05 05:58:48 +00:00
parent cc873357a6
commit d659274b50
17 changed files with 1607 additions and 861 deletions

View file

@ -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

View file

@ -42,7 +42,6 @@
#include "channels.h"
#include "dispatch.h"
#include "transports.h"
#include "pes.h"
#include "buffer.h"

View file

@ -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;

View file

@ -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);

14
mux.c
View file

@ -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;

238
parser_h264.c Normal file
View file

@ -0,0 +1,238 @@
/*
* h264 SPS / PPS Parser
*
* Based on h264.c from ffmpeg (www.ffmpeg.org)
*
* Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at>
*
* 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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#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;i<size;i++){
if(next)
next = (last + read_golomb_se(bs)) & 0xff;
if(!i && !next)
break; /* matrix not written */
last = next ? next : last;
}
}
int
h264_decode_seq_parameter_set(th_stream_t *st, bitstream_t *bs)
{
// h264_private_t *priv = st->st_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;
}
}

26
parser_h264.h Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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_ */

811
parsers.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ffmpeg/avcodec.h>
#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);
}

View file

@ -16,15 +16,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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 */

433
pes.c
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ffmpeg/avcodec.h>
#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);
}

5
psi.c
View file

@ -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;

View file

@ -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);

View file

@ -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;
}
}

694
tsmux.c
View file

@ -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++;
}

18
tsmux.h
View file

@ -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;

View file

@ -27,6 +27,7 @@
#include <libhts/htscfg.h>
#include <libhts/avg.h>
#include "refstr.h"
#include <ffmpeg/avcodec.h>
/*
* 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);

3
v4l.c
View file

@ -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;
}