added first version of MPEG/DVB transport stream demux
Originally committed as revision 544 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
		
							parent
							
								
									4f12a4976a
								
							
						
					
					
						commit
						fe9cf0d44e
					
				
							
								
								
									
										316
									
								
								libav/mpegts.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								libav/mpegts.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,316 @@ | |||||||
|  | /*
 | ||||||
|  |  * 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; | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user