/*
* ISO 13818 stream multiplexer
* Copyright (C) 2001 Convergence Integrated Media GmbH Berlin
* Author: Oskar Schirmer (oskar@convergence.de)
*/
/*
* Module: Split PES
* Purpose: Split a packetized elementary stream.
*
* This module examines a packetized elementary stream and copies the
* packets to the input stream buffer. Some of the service functions
* provided are also used by module Split PS due to similarity of the
* format.
*/
#include "global.h"
#include "error.h"
#include "pes.h"
#include "splitpes.h"
#include "input.h"
#include "splice.h"
/* Guess a stream type.
* Return: a stream type according to ISO 13818-1 table 2-29, 0 if none such
*/
int guess_streamtype (int streamid)
{
if ((streamid >= PES_CODE_AUDIO)
&& (streamid < PES_CODE_AUDIO + PES_NUMB_AUDIO)) {
return (PES_STRTYP_AUDIO13818);
}
if ((streamid >= PES_CODE_VIDEO)
&& (streamid < PES_CODE_VIDEO + PES_NUMB_VIDEO)) {
return (PES_STRTYP_VIDEO13818);
}
switch (streamid) {
case PES_CODE_PRIVATE1:
case PES_CODE_PRIVATE2:
return (PES_STRTYP_PRIVATDATA);
case PES_CODE_DSMCC:
return (PES_STRTYP_DSMCC);
case PES_CODE_ITU222A:
case PES_CODE_ITU222B:
case PES_CODE_ITU222C:
case PES_CODE_ITU222D:
case PES_CODE_ITU222E:
return (PES_STRTYP_ITUH222);
case PES_CODE_ISO13522:
return (PES_STRTYP_MHEG13522);
default:
return (0);
}
}
/* Skip to find a PES/PS stream prefix
* Precondition: f!=NULL
* Postcondition: if found: f->data.out indicates the prefix.
* Return: TRUE, if found, FALSE otherwise.
*/
boolean pes_skip_to_prefix (file_descr *f)
{
unsigned int p;
int i, l, k;
boolean r = FALSE;
l = k = list_size (f->data);
i = f->data.out;
p = 2;
while ((l > 0)
&& ((p != 0x000001) || (r = TRUE, FALSE))) {
p = ((p & 0x0000FFFF) << 8) | f->data.ptr[i];
list_incr (i,f->data,1);
l -= 1;
}
k = k - l - PES_SYNC_SIZE;
if (k > 0) {
warn (LWAR,"Skipped",EPES,1,1,k);
f->skipped += k; /* evaluate: skip > good and skip > CONST -> bad */
f->total += k;
list_incr (f->data.out,f->data,k);
}
return (r);
}
/* Determine the stream id of a packet.
* Precondition: d!=NULL points to a package.
* Return: stream id.
*/
int pes_stream_id (refr_data *d)
{
int i;
i = d->out;
list_incr (i,*d,PES_STREAM_ID);
warn (LINF,"Stream Id",EPES,2,1,d->ptr[i]);
return (d->ptr[i]);
}
/* Determine the length of a packet.
* Precondition: d!=NULL points to a package.
* Return: packet length.
*/
int pes_packet_length (refr_data *d)
{ /* special case len = 0: to do 2.4.3.7 */
#define MAX_PACKETSIZE_PROCESSABLE \
(mmin((MAX_DATA_RAWB-HIGHWATER_RAW),(MAX_DATA_INB/2)) - PES_HEADER_SIZE)
int i;
__u16 l;
i = d->out;
list_incr (i,*d,PES_PACKET_LENGTH);
l = d->ptr[i] << 8;
list_incr (i,*d,1);
l = l | d->ptr[i];
if (l > MAX_PACKETSIZE_PROCESSABLE) {
warn (LWAR,"Packet too large",EPES,3,2,l);
l = MAX_PACKETSIZE_PROCESSABLE;
}
warn (LINF,"Packet Length",EPES,3,1,l);
return (l);
}
/* Copy data from a raw data input buffer to a stream buffer.
* wrapping is done for the raw data input buffer.
* If the data does not fit at the end of the buffer, break to the start
* of the buffer and put it there.
* Precondition: src!=NULL providing at least size bytes
* dst!=NULL with free unbroken space of a least size bytes.
* Return: Pointer to copied data.
*/
int pes_transfer (refr_data *src,
refr_data *dst,
int size)
{
int l, r;
if (size > list_freeinend (*dst)) {
dst->in = 0;
}
r = dst->in;
while (size > 0) {
l = MAX_DATA_RAWB - src->out;
if (l > size) {
l = size;
}
memcpy (&dst->ptr[dst->in],&src->ptr[src->out],l);
list_incr (dst->in,*dst,l);
list_incr (src->out,*src,l);
warn (LDEB,"Transfer",EPES,4,1,l);
size -= l;
}
return (r);
}
/* Split data from a PES stream.
* Precondition: f!=NULL
* Return: TRUE, if something was processed, FALSE if no data/space available
*/
boolean split_pes (file_descr *f)
{
int l, p, q;
stream_descr *s;
ctrl_buffer *c;
warn (LDEB,"Split PES",EPES,0,0,f);
if (pes_skip_to_prefix (f)) {
l = list_size (f->data);
if (l >= PES_HDCODE_SIZE) {
p = pes_stream_id (&f->data);
if (p >= PES_LOWEST_SID) {
if (f->u.pes.stream == NULL) {
if (f->automatic) {
f->u.pes.stream = connect_streamprog (f,f->auto_programnb,
p,-p,guess_streamtype(p),NULL,NULL,FALSE);
if (f->u.pes.stream == NULL) {
f->automatic = FALSE;
return (FALSE);
}
} else {
if (list_free (f->data) < HIGHWATER_RAW) {
if (!S_ISREG(f->st_mode)) {
f->skipped += PES_SYNC_SIZE;
f->total += PES_SYNC_SIZE;
list_incr (f->data.out,f->data,PES_SYNC_SIZE);
return (TRUE);
}
}
return (FALSE);
}
}
s = f->u.pes.stream;
if (l >= PES_HEADER_SIZE) {
q = pes_packet_length (&f->data);
q += PES_HEADER_SIZE;
if (l >= q) {
if (p == s->stream_id) {
if (list_free (s->data) >= 2*q-1) {
c = &s->ctrl.ptr[s->ctrl.in];
c->length = q;
f->payload += q;
f->total += q;
c->index = pes_transfer (&f->data,&s->data,q);
warn (LDEB,"Sequence",EPES,0,1,f->sequence);
c->sequence = f->sequence++;
c->scramble = 0;
c->msecread = msec_now ();
if (S_ISREG (f->st_mode)) {
c->msecpush = c->msecread; /* wrong, but how ? */
} else {
c->msecpush = c->msecread; /* enough ? */
}
c->pcr.valid = FALSE;
c->opcr.valid = FALSE;
list_incr (s->ctrl.in,s->ctrl,1);
return (TRUE);
}
} else {
warn (LDEB,"Dropped PES Packet",EPES,0,p,q);
f->skipped += q;
f->total += q;
list_incr (f->data.out,f->data,q);
return (TRUE);
}
}
}
} else {
warn (LWAR,"Unknown PES Packet",EPES,0,2,p);
f->skipped += PES_SYNC_SIZE;
f->total += PES_SYNC_SIZE;
list_incr (f->data.out,f->data,PES_SYNC_SIZE);
return (TRUE);
}
}
}
return (FALSE);
}
LinuxTV legacy CVS <linuxtv.org/cvs>