tvheadend/pes.c
2008-01-08 09:47:35 +00:00

376 lines
8.5 KiB
C

/*
* 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);
static void pes_compute_duration(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_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
*/
static 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);
}