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:
parent
cc873357a6
commit
d659274b50
17 changed files with 1607 additions and 861 deletions
4
Makefile
4
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
|
||||
|
||||
|
|
1
avgen.c
1
avgen.c
|
@ -42,7 +42,6 @@
|
|||
#include "channels.h"
|
||||
#include "dispatch.h"
|
||||
#include "transports.h"
|
||||
#include "pes.h"
|
||||
#include "buffer.h"
|
||||
|
||||
|
||||
|
|
8
buffer.c
8
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;
|
||||
|
|
73
file_input.c
73
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);
|
||||
|
|
14
mux.c
14
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;
|
||||
|
|
238
parser_h264.c
Normal file
238
parser_h264.c
Normal 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
26
parser_h264.h
Normal 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
811
parsers.c
Normal 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);
|
||||
}
|
|
@ -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
433
pes.c
|
@ -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
5
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;
|
||||
|
|
22
transports.c
22
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);
|
||||
|
|
|
@ -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
694
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++;
|
||||
}
|
||||
|
|
18
tsmux.h
18
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;
|
||||
|
||||
|
|
98
tvhead.h
98
tvhead.h
|
@ -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
3
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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue