mirror of https://github.com/FFmpeg/FFmpeg.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
9.6 KiB
316 lines
9.6 KiB
/* |
|
* MPEG2 transport stream (aka DVB) demux |
|
* Copyright (c) 2002 Gerard Lantau. |
|
* |
|
* 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 2 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, write to the Free Software |
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|
*/ |
|
#include "avformat.h" |
|
|
|
#define TS_FEC_PACKET_SIZE 204 |
|
#define TS_PACKET_SIZE 188 |
|
#define NB_PID_MAX 8192 |
|
|
|
enum MpegTSState { |
|
MPEGTS_HEADER = 0, |
|
MPEGTS_PESHEADER_FILL, |
|
MPEGTS_PESHEADER_FLAGS, |
|
MPEGTS_PESHEADER_SIZE, |
|
MPEGTS_PESHEADER_READ, |
|
MPEGTS_PAYLOAD, |
|
MPEGTS_SKIP, |
|
}; |
|
|
|
/* enough for PES header + length */ |
|
#define MAX_HEADER_SIZE 6 |
|
|
|
typedef struct MpegTSStream { |
|
int pid; |
|
enum MpegTSState state; |
|
int last_cc; /* last cc code (-1 if first packet) */ |
|
/* used to get the format */ |
|
int header_size; |
|
int payload_size; |
|
int pes_header_size; |
|
AVStream *st; |
|
unsigned char header[MAX_HEADER_SIZE]; |
|
} MpegTSStream; |
|
|
|
typedef struct MpegTSContext { |
|
int raw_packet_size; /* raw packet size, including FEC if present */ |
|
MpegTSStream *pids[NB_PID_MAX]; |
|
} MpegTSContext; |
|
|
|
/* autodetect fec presence. Must have at least 1024 bytes */ |
|
static int get_packet_size(const unsigned char *buf, int size) |
|
{ |
|
int i; |
|
|
|
if (size < (TS_FEC_PACKET_SIZE * 5 + 1)) |
|
return -1; |
|
for(i=0;i<5;i++) { |
|
if (buf[i * TS_PACKET_SIZE] != 0x47) |
|
goto try_fec; |
|
} |
|
return TS_PACKET_SIZE; |
|
try_fec: |
|
for(i=0;i<5;i++) { |
|
if (buf[i * TS_FEC_PACKET_SIZE] != 0x47) |
|
return -1; |
|
} |
|
return TS_FEC_PACKET_SIZE; |
|
} |
|
|
|
static int mpegts_probe(AVProbeData *p) |
|
{ |
|
int size; |
|
size = get_packet_size(p->buf, p->buf_size); |
|
if (size < 0) |
|
return 0; |
|
return AVPROBE_SCORE_MAX; |
|
} |
|
|
|
static int mpegts_read_header(AVFormatContext *s, |
|
AVFormatParameters *ap) |
|
{ |
|
MpegTSContext *ts = s->priv_data; |
|
ByteIOContext *pb = &s->pb; |
|
unsigned char buf[1024]; |
|
int len; |
|
INT64 pos; |
|
|
|
/* read the first 1024 bytes to get packet size */ |
|
pos = url_ftell(pb); |
|
len = get_buffer(pb, buf, sizeof(buf)); |
|
if (len != sizeof(buf)) |
|
goto fail; |
|
ts->raw_packet_size = get_packet_size(buf, sizeof(buf)); |
|
if (ts->raw_packet_size <= 0) |
|
goto fail; |
|
/* go again to the start */ |
|
url_fseek(pb, pos, SEEK_SET); |
|
return 0; |
|
fail: |
|
return -1; |
|
} |
|
|
|
/* return non zero if a packet could be constructed */ |
|
static int mpegts_push_data(AVFormatContext *s, MpegTSStream *tss, |
|
AVPacket *pkt, |
|
const unsigned char *buf, int buf_size, int is_start) |
|
{ |
|
AVStream *st; |
|
const unsigned char *p; |
|
int len, code, codec_type, codec_id; |
|
|
|
if (is_start) { |
|
tss->state = MPEGTS_HEADER; |
|
tss->header_size = 0; |
|
} |
|
p = buf; |
|
while (buf_size > 0) { |
|
len = buf_size; |
|
switch(tss->state) { |
|
case MPEGTS_HEADER: |
|
if (len > MAX_HEADER_SIZE - tss->header_size) |
|
len = MAX_HEADER_SIZE - tss->header_size; |
|
memcpy(tss->header, p, len); |
|
tss->header_size += len; |
|
p += len; |
|
buf_size -= len; |
|
if (tss->header_size == MAX_HEADER_SIZE) { |
|
/* we got all the PES or section header. We can now |
|
decide */ |
|
#if 0 |
|
av_hex_dump(tss->header, tss->header_size); |
|
#endif |
|
if (tss->header[0] == 0x00 && tss->header[1] == 0x00 && |
|
tss->header[2] == 0x01) { |
|
/* it must be an mpeg2 PES stream */ |
|
/* XXX: add AC3 support */ |
|
code = tss->header[3] | 0x100; |
|
if (!((code >= 0x1c0 && code <= 0x1df) || |
|
(code >= 0x1e0 && code <= 0x1ef))) |
|
goto skip; |
|
if (!tss->st) { |
|
/* allocate stream */ |
|
if (code >= 0x1c0 && code <= 0x1df) { |
|
codec_type = CODEC_TYPE_AUDIO; |
|
codec_id = CODEC_ID_MP2; |
|
} else { |
|
codec_type = CODEC_TYPE_VIDEO; |
|
codec_id = CODEC_ID_MPEG1VIDEO; |
|
} |
|
st = av_new_stream(s, tss->pid); |
|
if (st) { |
|
st->priv_data = tss; |
|
st->codec.codec_type = codec_type; |
|
st->codec.codec_id = codec_id; |
|
tss->st = st; |
|
} |
|
} |
|
tss->state = MPEGTS_PESHEADER_FILL; |
|
tss->payload_size = (tss->header[4] << 8) | tss->header[5]; |
|
if (tss->payload_size == 0) |
|
tss->payload_size = 65536; |
|
} else { |
|
/* otherwise, it should be a table */ |
|
/* skip packet */ |
|
skip: |
|
tss->state = MPEGTS_SKIP; |
|
continue; |
|
} |
|
} |
|
break; |
|
/**********************************************/ |
|
/* PES packing parsing */ |
|
case MPEGTS_PESHEADER_FILL: |
|
/* skip filling */ |
|
code = *p++; |
|
buf_size--; |
|
tss->payload_size--; |
|
if (code != 0xff) { |
|
if ((code & 0xc0) != 0x80) |
|
goto skip; |
|
tss->state = MPEGTS_PESHEADER_FLAGS; |
|
} |
|
break; |
|
case MPEGTS_PESHEADER_FLAGS: |
|
code = *p++; |
|
buf_size--; |
|
tss->payload_size--; |
|
tss->state = MPEGTS_PESHEADER_SIZE; |
|
break; |
|
case MPEGTS_PESHEADER_SIZE: |
|
tss->pes_header_size = *p++; |
|
buf_size--; |
|
tss->payload_size--; |
|
tss->state = MPEGTS_PESHEADER_READ; |
|
break; |
|
case MPEGTS_PESHEADER_READ: |
|
/* currently we do nothing except skipping */ |
|
if (len > tss->pes_header_size) |
|
len = tss->pes_header_size; |
|
p += len; |
|
buf_size -= len; |
|
tss->pes_header_size -= len; |
|
tss->payload_size -= len; |
|
if (tss->pes_header_size == 0) |
|
tss->state = MPEGTS_PAYLOAD; |
|
break; |
|
case MPEGTS_PAYLOAD: |
|
if (len > tss->payload_size) |
|
len = tss->payload_size; |
|
if (len > 0) { |
|
if (tss->st && av_new_packet(pkt, buf_size) == 0) { |
|
memcpy(pkt->data, p, buf_size); |
|
pkt->stream_index = tss->st->index; |
|
return 1; |
|
} |
|
tss->payload_size -= len; |
|
} |
|
buf_size = 0; |
|
break; |
|
case MPEGTS_SKIP: |
|
buf_size = 0; |
|
break; |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
static int mpegts_read_packet(AVFormatContext *s, |
|
AVPacket *pkt) |
|
{ |
|
MpegTSContext *ts = s->priv_data; |
|
MpegTSStream *tss; |
|
ByteIOContext *pb = &s->pb; |
|
unsigned char packet[TS_FEC_PACKET_SIZE]; |
|
int len, pid, cc, cc_ok, afc; |
|
const unsigned char *p; |
|
|
|
for(;;) { |
|
len = get_buffer(pb, packet, ts->raw_packet_size); |
|
if (len != ts->raw_packet_size) |
|
return AVERROR_IO; |
|
/* check paquet sync byte */ |
|
/* XXX: accept to resync ? */ |
|
if (packet[0] != 0x47) |
|
return AVERROR_INVALIDDATA; |
|
|
|
pid = ((packet[1] & 0x1f) << 8) | packet[2]; |
|
tss = ts->pids[pid]; |
|
if (tss == NULL) { |
|
/* if no pid found, then add a pid context */ |
|
tss = av_mallocz(sizeof(MpegTSStream)); |
|
if (!tss) |
|
continue; |
|
ts->pids[pid] = tss; |
|
tss->pid = pid; |
|
tss->last_cc = -1; |
|
// printf("new pid=0x%x\n", pid); |
|
} |
|
|
|
/* continuity check (currently not used) */ |
|
cc = (packet[3] & 0xf); |
|
cc_ok = (tss->last_cc < 0) || ((((tss->last_cc + 1) & 0x0f) == cc)); |
|
tss->last_cc = cc; |
|
|
|
/* skip adaptation field */ |
|
afc = (packet[3] >> 4) & 3; |
|
p = packet + 4; |
|
if (afc == 0) /* reserved value */ |
|
continue; |
|
if (afc == 2) /* adaptation field only */ |
|
continue; |
|
if (afc == 3) { |
|
/* skip adapation field */ |
|
p += p[0] + 1; |
|
} |
|
/* if past the end of packet, ignore */ |
|
if (p >= packet + TS_PACKET_SIZE) |
|
continue; |
|
|
|
if (mpegts_push_data(s, tss, pkt, p, TS_PACKET_SIZE - (p - packet), |
|
packet[1] & 0x40)) |
|
break; |
|
} |
|
return 0; |
|
} |
|
|
|
static int mpegts_read_close(AVFormatContext *s) |
|
{ |
|
MpegTSContext *ts = s->priv_data; |
|
int i; |
|
for(i=0;i<NB_PID_MAX;i++) |
|
av_freep(ts->pids[i]); |
|
return 0; |
|
} |
|
|
|
AVInputFormat mpegts_demux = { |
|
"mpegts", |
|
"MPEG2 transport stream format", |
|
sizeof(MpegTSContext), |
|
mpegts_probe, |
|
mpegts_read_header, |
|
mpegts_read_packet, |
|
mpegts_read_close, |
|
flags: AVFMT_NOHEADER | AVFMT_SHOW_IDS, |
|
}; |
|
|
|
int mpegts_init(void) |
|
{ |
|
av_register_input_format(&mpegts_demux); |
|
return 0; |
|
}
|
|
|