/* * WARNING: Quite a hack was required in order to get files by MultiDec played back correctly. * If it breaks anything else, just comment out the "#define DEMUX_PVA_MULTIDEC_HACK" below * and it will not be compiled in. * * Feedback is appreciated. * * written by Matteo Giani */ /* * MPEG2 transport stream (aka DVB) demux * Copyright (c) 2002 Fabrice Bellard. * * This library 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 of the License, or (at your option) any later version. * * This library 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 this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "config.h" #include "mp_msg.h" #include "help_mp.h" #include "stream.h" #include "demuxer.h" #include "stheader.h" #include "bswap.h" typedef struct { off_t offset; long size; uint8_t type; uint8_t is_packet_start; float pts; uint8_t packet_size; } ts_payload_t; typedef struct { float last_audio_pts; float last_video_pts; uint8_t just_synced; uint8_t synced_stream_id; char *buffer; int buffer_size; int buffer_offset; int packet_size; } ts_priv_t; FILE *outfile; //#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 //#define MAX_HEADER_SIZE 14 typedef struct MpegTSStream // IT'S AN ES { 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; enum { VIDEO = 0, AUDIO } type; unsigned char header[MAX_HEADER_SIZE]; char *pes_buffer; int offset; } MpegTSStream; typedef struct MpegTSContext { int raw_packet_size; /* raw packet size, including FEC if present */ MpegTSStream *pids[NB_PID_MAX]; demuxer_t *demuxer; } MpegTSContext; 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; } int ts_check_file(demuxer_t * demuxer) { int buf_size = TS_FEC_PACKET_SIZE * 5 + 1; char buf[buf_size]; int _read, size; char *buf2 = ((ts_priv_t*)demuxer->priv)->buffer; //da usare al posto di buf perche' senno' ci perdiamo i byte mp_msg(MSGT_DEMUX, MSGL_V, "*************Checking for TS************\n"); _read = stream_read(demuxer->stream, buf, buf_size); stream_seek(demuxer->stream, 0); if(_read < buf_size) { mp_msg(MSGT_DEMUX, MSGL_V, "NOT A TS FILE\n"); return 0; } size = get_packet_size(buf, buf_size); if (size < 0) return 0; else return size; } demuxer_t * demux_open_ts(demuxer_t * demuxer) { int packet_size; demuxer_t *vd, *ad; stream_t *s; sh_video_t *sh_video; sh_audio_t *sh_audio; ts_priv_t * priv; demuxer->type= DEMUXER_TYPE_TS; demuxer->seekable = 0; stream_reset(demuxer->stream); stream_seek(demuxer->stream, 0); if(! (packet_size = ts_check_file(demuxer))) return NULL; priv = malloc(sizeof(ts_priv_t)); priv->last_video_pts=-1; priv->last_audio_pts=-1; priv->packet_size = packet_size; demuxer->priv = priv; //if(demuxer->stream->type != STREAMTYPE_FILE) demuxer->seekable=0; //else demuxer->seekable = 1; sh_video = new_sh_video(demuxer, 0); sh_video->ds = demuxer->video; demuxer->video->sh = sh_video; /* THIS IS ACTUALLY USELESS, WE DON'T USE IT */ sh_audio = new_sh_audio(demuxer, 0); sh_audio->ds = demuxer->audio; demuxer->audio->sh = sh_audio; mp_msg(MSGT_DEMUXER,MSGL_INFO, "Opened TS demuxer...\n"); /* demuxer->movi_start = 0; demuxer->movi_end = demuxer->stream->end_pos; */ s = new_ds_stream(demuxer->video); outfile=fopen("XX.mpg", "w"); return demux_open_stream(s, DEMUXER_TYPE_MPEG_PS, -1, -1, -1, NULL); } void demux_close_ts(demuxer_t * demuxer) { if(demuxer->priv) { free(demuxer->priv); demuxer->priv=NULL; } } static int pes_parse(MpegTSStream *tss, const unsigned char *buf, int buf_size, int is_start) { 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; mp_msg(MSGT_DEMUX, MSGL_V, "LEN: %d\n", len); memcpy(tss->header, p, len); tss->header_size += len; p += len; buf_size -= len; if (tss->header_size == MAX_HEADER_SIZE) //abbiamo letto tutto l'header { /* we got all the PES or section header. We can now decide */ 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; /* allocate stream */ if (code >= 0x1c0 && code <= 0x1df) { tss->type = AUDIO; } else { tss->type = VIDEO; } tss->state = MPEGTS_PESHEADER_FILL; tss->payload_size = (tss->header[4] << 8) | tss->header[5]; if (tss->payload_size == 0) tss->payload_size = 65536; //fprintf(stderr, "PID: %u, PAYLOADSIZE: %u\n", code, tss->payload_size); } else { /* otherwise, it should be a table */ /* skip packet */ skip: tss->state = MPEGTS_SKIP; continue; } } break; /**********************************************/ /* PES packing parsing */ case MPEGTS_PESHEADER_FILL: mp_msg(MSGT_DEMUX, MSGL_DBG2, "PES_HEADER_FILL\n"); /* 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: mp_msg(MSGT_DEMUX, MSGL_DBG2, "PES_HEADER_FLAGS\n"); code = *p++; buf_size--; tss->payload_size--; tss->state = MPEGTS_PESHEADER_SIZE; break; case MPEGTS_PESHEADER_SIZE: mp_msg(MSGT_DEMUX, MSGL_DBG2, "PES_HEADER_SIZE\n"); tss->pes_header_size = *p++; buf_size--; tss->payload_size--; tss->state = MPEGTS_PESHEADER_READ; break; case MPEGTS_PESHEADER_READ: mp_msg(MSGT_DEMUX, MSGL_DBG2, "PES_HEADER_READ\n"); /* 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(((tss->type == VIDEO ) || (tss->type == AUDIO)) && is_start) mp_msg(MSGT_DEMUX, MSGL_V, "BUF: %x, %02X %02X %02X %02X, P: %x, DIFF: %d, IS_START: %d, TYPE: %d, STRING: %02X %02X %02X %02X\n", buf, buf[0], buf[1], buf[2], buf[3], p, p-buf, is_start, tss->type, p[0], p[1], p[2], p[3]); if (len > tss->payload_size) len = tss->payload_size; if (len > 0) { if(is_start && (buf[7] & 0x80)) { uint64_t pts; pts = (int64_t)(buf[9] & 0x0E) << 29 ; pts |= buf[10] << 22 ; pts |= (buf[11] & 0xFE) << 14 ; pts |= buf[12] << 7 ; pts |= (buf[13] & 0xFE) >> 1 ; mp_msg(MSGT_DEMUX, MSGL_V, "PTS: %lf\n", pts/90000.0f); } else mp_msg(MSGT_DEMUX, MSGL_DBG2, "NO PTS: %d\n", buf[7] >> 6); return p; } buf_size = 0; break; case MPEGTS_SKIP: mp_msg(MSGT_DEMUX, MSGL_DBG2, "TS_SKIP\n"); buf_size = 0; break; } } return 0; } // 0 = EOF or no stream found // 1 = successfully read a packet int demux_ts_fill_buffer (demuxer_t * demuxer) { MpegTSContext ts; MpegTSStream *tss; uint8_t done = 0, filled = 0; demux_packet_t *dp; ts_priv_t *priv = demuxer->priv; //ts_payload_t payload; uint16_t buf_size, i, is_start = 0; char *es_start; unsigned char packet[TS_FEC_PACKET_SIZE]; int len, pid, cc, cc_ok, afc; const unsigned char *p; while(!done) { //len = get_buffer(pb, packet, ts->raw_packet_size); len = stream_read(demuxer->stream, packet, priv->packet_size); if (len != priv->packet_size) return 0; /* check packet sync byte */ /* XXX: accept to resync ? */ if (packet[0] != 0x47) { mp_msg(MSGT_DEMUX, MSGL_V, "NON INIZIA CON UN TS-SIGN\n"); return 0; } pid = ((packet[1] & 0x1f) << 8) | packet[2]; tss = ts.pids[pid]; //an ES stream if (tss == NULL) { /* if no pid found, then add a pid context */ tss = malloc(sizeof(MpegTSStream)); if (!tss) continue; memset(tss, 0, sizeof(MpegTSStream)); ts.pids[pid] = tss; tss->pid = pid; tss->last_cc = -1; mp_msg(MSGT_DEMUX, MSGL_V, "new pid=%u\n", pid); } 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; //p is the beginning of the TS payload == PES packet fragment 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; // da qui c' il contenuto di un pes, la funzione che segue ci dara' il tipo del payload buf_size = TS_PACKET_SIZE - (p - packet); //LENGTH OF THE TS PAYLOAD is_start = packet[1] & 0x40; if(es_start = pes_parse(tss, p, buf_size, is_start)) { tss->offset += buf_size; filled = 1; if((tss->type == VIDEO) || (tss->type == AUDIO)) { dp = new_demux_packet(buf_size); memcpy(dp->buffer, p, buf_size); dp->pts = 0; dp->flags = 0; dp->pos = stream_tell(demuxer->stream); ds_add_packet(demuxer->video, dp); fwrite(p, 1, buf_size, outfile); mp_msg(MSGT_DEMUX, MSGL_V, "ADDED %d bytes of PES type %s to the video fifo starting from %X\n", buf_size, (tss->type ? "AUDIO" : "VIDEO"), es_start); return buf_size; } } } } int demux_seek_ts(demuxer_t * demuxer, float rel_seek_secs, int flags) { return 0; } static int mpegts_read_close(MpegTSContext *ts) { int i; for(i=0;ipids[i]); return 0; } /* static int mpegts_read_header(AVFormatContext *s, AVFormatParameters *ap) { MpegTSContext *ts = s->priv_data; demuxer_t *demuxer = ts->demuxer; 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); pos = stream_tell(demuxer->stream); 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); stream_seek(demuxer->stream, pos); //, SEEK_SET); return 0; fail: return -1; } static int ts_stream_read(demuxer_t *demuxer, char *buf, int size) { ts_priv_t *priv = demuxer->priv; int s; if(priv->buffer && priv->buffer_size) { int avl = priv->buffer_size - priv->buffer_offset; if(avl >= size) { memcpy(buf, &(priv->buffer[priv->buffer_offset]), size); priv->buffer_offset += size; if(! priv->buffer_size) free(priv->buffer); return size; } else { memcpy(buf, priv->buffer, priv->buffer_size); s = stream_read(demuxer->stream, &buf[priv->buffer_size], size-priv->buffer_size); free(priv->buffer); priv->buffer_size = 0; return s; } } else return stream_read(demuxer->stream, buf, size); } static int ts_sync(demuxer_t *demuxer) { ts_priv_t *priv = demuxer->priv; char *buf; int psize = priv->packet_size int size = 5 * psize; buf = malloc(size); if(! buf) return NULL; if(ts_stream_read(demuxer->stream, buf, size) < size) return NULL; found = i = 0; while((! found) && (i < psize)) { if((buf[i] == 0x47) && (buf[i + psize] == 0x47) && (buf[i + 2*psize] == 0x47) && (buf[i + 3*psize] == 0x47) && (buf[i + 4*psize] == 0x47)) found=1; if(found) continue; i++; } if(found) return i; else return -1; } */