LCOV - code coverage report
Current view: top level - libavformat - ty.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 4 361 1.1 %
Date: 2017-12-11 04:34:20 Functions: 1 12 8.3 %

          Line data    Source code
       1             : /*
       2             :  * TiVo ty stream demuxer
       3             :  * Copyright (c) 2005 VLC authors and VideoLAN
       4             :  * Copyright (c) 2005 by Neal Symms (tivo@freakinzoo.com) - February 2005
       5             :  * based on code by Christopher Wingert for tivo-mplayer
       6             :  * tivo(at)wingert.org, February 2003
       7             :  * Copyright (c) 2017 Paul B Mahol
       8             :  *
       9             :  * This file is part of FFmpeg.
      10             :  *
      11             :  * FFmpeg is free software; you can redistribute it and/or
      12             :  * modify it under the terms of the GNU Lesser General Public
      13             :  * License as published by the Free Software Foundation; either
      14             :  * version 2.1 of the License, or (at your option) any later version.
      15             :  *
      16             :  * FFmpeg is distributed in the hope that it will be useful,
      17             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      18             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      19             :  * Lesser General Public License for more details.
      20             :  *
      21             :  * You should have received a copy of the GNU Lesser General Public
      22             :  * License along with FFmpeg; if not, write to the Free Software
      23             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      24             :  */
      25             : 
      26             : #include "libavutil/intreadwrite.h"
      27             : #include "avformat.h"
      28             : #include "internal.h"
      29             : #include "mpeg.h"
      30             : 
      31             : #define SERIES1_PES_LENGTH  11    /* length of audio PES hdr on S1 */
      32             : #define SERIES2_PES_LENGTH  16    /* length of audio PES hdr on S2 */
      33             : #define AC3_PES_LENGTH      14    /* length of audio PES hdr for AC3 */
      34             : #define VIDEO_PES_LENGTH    16    /* length of video PES header */
      35             : #define DTIVO_PTS_OFFSET    6     /* offs into PES for MPEG PTS on DTivo */
      36             : #define SA_PTS_OFFSET       9     /* offset into PES for MPEG PTS on SA */
      37             : #define AC3_PTS_OFFSET      9     /* offset into PES for AC3 PTS on DTivo */
      38             : #define VIDEO_PTS_OFFSET    9     /* offset into PES for video PTS on all */
      39             : #define AC3_PKT_LENGTH      1536  /* size of TiVo AC3 pkts (w/o PES hdr) */
      40             : 
      41             : static const uint8_t ty_VideoPacket[]     = { 0x00, 0x00, 0x01, 0xe0 };
      42             : static const uint8_t ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
      43             : static const uint8_t ty_AC3AudioPacket[]  = { 0x00, 0x00, 0x01, 0xbd };
      44             : 
      45             : #define TIVO_PES_FILEID   0xf5467abd
      46             : #define CHUNK_SIZE        (128 * 1024)
      47             : #define CHUNK_PEEK_COUNT  3      /* number of chunks to probe */
      48             : 
      49             : typedef struct TyRecHdr {
      50             :     int64_t   rec_size;
      51             :     uint8_t   ex[2];
      52             :     uint8_t   rec_type;
      53             :     uint8_t   subrec_type;
      54             :     uint64_t  ty_pts;            /* TY PTS in the record header */
      55             : } TyRecHdr;
      56             : 
      57             : typedef enum {
      58             :     TIVO_TYPE_UNKNOWN,
      59             :     TIVO_TYPE_SA,
      60             :     TIVO_TYPE_DTIVO
      61             : } TiVo_type;
      62             : 
      63             : typedef enum {
      64             :     TIVO_SERIES_UNKNOWN,
      65             :     TIVO_SERIES1,
      66             :     TIVO_SERIES2
      67             : } TiVo_series;
      68             : 
      69             : typedef enum {
      70             :     TIVO_AUDIO_UNKNOWN,
      71             :     TIVO_AUDIO_AC3,
      72             :     TIVO_AUDIO_MPEG
      73             : } TiVo_audio;
      74             : 
      75             : typedef struct TySeqTable {
      76             :     uint64_t    timestamp;
      77             :     uint8_t     chunk_bitmask[8];
      78             : } TySeqTable;
      79             : 
      80             : typedef struct TYDemuxContext {
      81             :     unsigned        cur_chunk;
      82             :     unsigned        cur_chunk_pos;
      83             :     int64_t         cur_pos;
      84             :     TiVo_type       tivo_type;        /* TiVo type (SA / DTiVo) */
      85             :     TiVo_series     tivo_series;      /* Series1 or Series2 */
      86             :     TiVo_audio      audio_type;       /* AC3 or MPEG */
      87             :     int             pes_length;       /* Length of Audio PES header */
      88             :     int             pts_offset;       /* offset into audio PES of PTS */
      89             :     uint8_t         pes_buffer[20];   /* holds incomplete pes headers */
      90             :     int             pes_buf_cnt;      /* how many bytes in our buffer */
      91             :     size_t          ac3_pkt_size;     /* length of ac3 pkt we've seen so far */
      92             :     uint64_t        last_ty_pts;      /* last TY timestamp we've seen */
      93             :     unsigned        seq_table_size;   /* number of entries in SEQ table */
      94             : 
      95             :     int64_t         first_audio_pts;
      96             :     int64_t         last_audio_pts;
      97             :     int64_t         last_video_pts;
      98             : 
      99             :     TyRecHdr       *rec_hdrs;         /* record headers array */
     100             :     int             cur_rec;          /* current record in this chunk */
     101             :     int             num_recs;         /* number of recs in this chunk */
     102             :     int             seq_rec;          /* record number where seq start is */
     103             :     TySeqTable     *seq_table;        /* table of SEQ entries from mstr chk */
     104             :     int             first_chunk;
     105             : 
     106             :     uint8_t         chunk[CHUNK_SIZE];
     107             : } TYDemuxContext;
     108             : 
     109        6130 : static int ty_probe(AVProbeData *p)
     110             : {
     111             :     int i;
     112             : 
     113       12319 :     for (i = 0; i + 12 < p->buf_size; i += CHUNK_SIZE) {
     114        6189 :         if (AV_RB32(p->buf + i) == TIVO_PES_FILEID &&
     115           0 :             AV_RB32(p->buf + i + 4) == 0x02 &&
     116           0 :             AV_RB32(p->buf + i + 8) == CHUNK_SIZE) {
     117           0 :             return AVPROBE_SCORE_MAX;
     118             :         }
     119             :     }
     120             : 
     121        6130 :     return 0;
     122             : }
     123             : 
     124           0 : static TyRecHdr *parse_chunk_headers(const uint8_t *buf,
     125             :                                      int num_recs)
     126             : {
     127             :     TyRecHdr *hdrs, *rec_hdr;
     128             :     int i;
     129             : 
     130           0 :     hdrs = av_calloc(num_recs, sizeof(TyRecHdr));
     131           0 :     if (!hdrs)
     132           0 :         return NULL;
     133             : 
     134           0 :     for (i = 0; i < num_recs; i++) {
     135           0 :         const uint8_t *record_header = buf + (i * 16);
     136             : 
     137           0 :         rec_hdr = &hdrs[i];     /* for brevity */
     138           0 :         rec_hdr->rec_type = record_header[3];
     139           0 :         rec_hdr->subrec_type = record_header[2] & 0x0f;
     140           0 :         if ((record_header[0] & 0x80) == 0x80) {
     141             :             uint8_t b1, b2;
     142             : 
     143             :             /* marker bit 2 set, so read extended data */
     144           0 :             b1 = (((record_header[0] & 0x0f) << 4) |
     145           0 :                   ((record_header[1] & 0xf0) >> 4));
     146           0 :             b2 = (((record_header[1] & 0x0f) << 4) |
     147           0 :                   ((record_header[2] & 0xf0) >> 4));
     148             : 
     149           0 :             rec_hdr->ex[0] = b1;
     150           0 :             rec_hdr->ex[1] = b2;
     151           0 :             rec_hdr->rec_size = 0;
     152           0 :             rec_hdr->ty_pts = 0;
     153             :         } else {
     154           0 :             rec_hdr->rec_size = (record_header[0] << 8 |
     155           0 :                                  record_header[1]) << 4 |
     156           0 :                                 (record_header[2] >> 4);
     157           0 :             rec_hdr->ty_pts = AV_RB64(&record_header[8]);
     158             :         }
     159             :     }
     160           0 :     return hdrs;
     161             : }
     162             : 
     163           0 : static int find_es_header(const uint8_t *header,
     164             :                           const uint8_t *buffer, int search_len)
     165             : {
     166             :     int count;
     167             : 
     168           0 :     for (count = 0; count < search_len; count++) {
     169           0 :         if (!memcmp(&buffer[count], header, 4))
     170           0 :             return count;
     171             :     }
     172           0 :     return -1;
     173             : }
     174             : 
     175           0 : static int analyze_chunk(AVFormatContext *s, const uint8_t *chunk)
     176             : {
     177           0 :     TYDemuxContext *ty = s->priv_data;
     178             :     int num_recs, i;
     179             :     TyRecHdr *hdrs;
     180             :     int num_6e0, num_be0, num_9c0, num_3c0;
     181             : 
     182             :     /* skip if it's a Part header */
     183           0 :     if (AV_RB32(&chunk[0]) == TIVO_PES_FILEID)
     184           0 :         return 0;
     185             : 
     186             :     /* number of records in chunk (we ignore high order byte;
     187             :      * rarely are there > 256 chunks & we don't need that many anyway) */
     188           0 :     num_recs = chunk[0];
     189           0 :     if (num_recs < 5) {
     190             :         /* try again with the next chunk.  Sometimes there are dead ones */
     191           0 :         return 0;
     192             :     }
     193             : 
     194           0 :     chunk += 4;       /* skip past rec count & SEQ bytes */
     195             :     ff_dlog(s, "probe: chunk has %d recs\n", num_recs);
     196           0 :     hdrs = parse_chunk_headers(chunk, num_recs);
     197           0 :     if (!hdrs)
     198           0 :         return AVERROR(ENOMEM);
     199             : 
     200             :     /* scan headers.
     201             :      * 1. check video packets.  Presence of 0x6e0 means S1.
     202             :      *    No 6e0 but have be0 means S2.
     203             :      * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
     204             :      *    If AC-3, then we have DTivo.
     205             :      *    If MPEG, search for PTS offset.  This will determine SA vs. DTivo.
     206             :      */
     207           0 :     num_6e0 = num_be0 = num_9c0 = num_3c0 = 0;
     208           0 :     for (i = 0; i < num_recs; i++) {
     209           0 :         switch (hdrs[i].subrec_type << 8 | hdrs[i].rec_type) {
     210           0 :         case 0x6e0:
     211           0 :             num_6e0++;
     212           0 :             break;
     213           0 :         case 0xbe0:
     214           0 :             num_be0++;
     215           0 :             break;
     216           0 :         case 0x3c0:
     217           0 :             num_3c0++;
     218           0 :             break;
     219           0 :         case 0x9c0:
     220           0 :             num_9c0++;
     221           0 :             break;
     222             :         }
     223             :     }
     224             :     ff_dlog(s, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.\n",
     225             :             num_6e0, num_be0);
     226             : 
     227             :     /* set up our variables */
     228           0 :     if (num_6e0 > 0) {
     229             :         ff_dlog(s, "detected Series 1 Tivo\n");
     230           0 :         ty->tivo_series = TIVO_SERIES1;
     231           0 :         ty->pes_length = SERIES1_PES_LENGTH;
     232           0 :     } else if (num_be0 > 0) {
     233             :         ff_dlog(s, "detected Series 2 Tivo\n");
     234           0 :         ty->tivo_series = TIVO_SERIES2;
     235           0 :         ty->pes_length = SERIES2_PES_LENGTH;
     236             :     }
     237           0 :     if (num_9c0 > 0) {
     238             :         ff_dlog(s, "detected AC-3 Audio (DTivo)\n");
     239           0 :         ty->audio_type = TIVO_AUDIO_AC3;
     240           0 :         ty->tivo_type = TIVO_TYPE_DTIVO;
     241           0 :         ty->pts_offset = AC3_PTS_OFFSET;
     242           0 :         ty->pes_length = AC3_PES_LENGTH;
     243           0 :     } else if (num_3c0 > 0) {
     244           0 :         ty->audio_type = TIVO_AUDIO_MPEG;
     245             :         ff_dlog(s, "detected MPEG Audio\n");
     246             :     }
     247             : 
     248             :     /* if tivo_type still unknown, we can check PTS location
     249             :      * in MPEG packets to determine tivo_type */
     250           0 :     if (ty->tivo_type == TIVO_TYPE_UNKNOWN) {
     251           0 :         uint32_t data_offset = 16 * num_recs;
     252             : 
     253           0 :         for (i = 0; i < num_recs; i++) {
     254           0 :             if (data_offset + hdrs[i].rec_size > CHUNK_SIZE)
     255           0 :                 break;
     256             : 
     257           0 :             if ((hdrs[i].subrec_type << 0x08 | hdrs[i].rec_type) == 0x3c0 && hdrs[i].rec_size > 15) {
     258             :                 /* first make sure we're aligned */
     259           0 :                 int pes_offset = find_es_header(ty_MPEGAudioPacket,
     260             :                         &chunk[data_offset], 5);
     261           0 :                 if (pes_offset >= 0) {
     262             :                     /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
     263           0 :                     if ((chunk[data_offset + 6 + pes_offset] & 0x80) == 0x80) {
     264             :                         /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
     265           0 :                         if (ty->tivo_series == TIVO_SERIES1)
     266             :                             ff_dlog(s, "detected Stand-Alone Tivo\n");
     267           0 :                         ty->tivo_type = TIVO_TYPE_SA;
     268           0 :                         ty->pts_offset = SA_PTS_OFFSET;
     269             :                     } else {
     270           0 :                         if (ty->tivo_series == TIVO_SERIES1)
     271             :                             ff_dlog(s, "detected DirecTV Tivo\n");
     272           0 :                         ty->tivo_type = TIVO_TYPE_DTIVO;
     273           0 :                         ty->pts_offset = DTIVO_PTS_OFFSET;
     274             :                     }
     275           0 :                     break;
     276             :                 }
     277             :             }
     278           0 :             data_offset += hdrs[i].rec_size;
     279             :         }
     280             :     }
     281           0 :     av_free(hdrs);
     282             : 
     283           0 :     return 0;
     284             : }
     285             : 
     286           0 : static int ty_read_header(AVFormatContext *s)
     287             : {
     288           0 :     TYDemuxContext *ty = s->priv_data;
     289           0 :     AVIOContext *pb = s->pb;
     290             :     AVStream *st, *ast;
     291           0 :     int i, ret = 0;
     292             : 
     293           0 :     ty->first_audio_pts = AV_NOPTS_VALUE;
     294           0 :     ty->last_audio_pts = AV_NOPTS_VALUE;
     295           0 :     ty->last_video_pts = AV_NOPTS_VALUE;
     296             : 
     297           0 :     for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
     298           0 :         avio_read(pb, ty->chunk, CHUNK_SIZE);
     299             : 
     300           0 :         ret = analyze_chunk(s, ty->chunk);
     301           0 :         if (ret < 0)
     302           0 :             return ret;
     303           0 :         if (ty->tivo_series != TIVO_SERIES_UNKNOWN &&
     304           0 :             ty->audio_type  != TIVO_AUDIO_UNKNOWN &&
     305           0 :             ty->tivo_type   != TIVO_TYPE_UNKNOWN)
     306           0 :             break;
     307             :     }
     308             : 
     309           0 :     if (ty->tivo_series == TIVO_SERIES_UNKNOWN ||
     310           0 :         ty->audio_type == TIVO_AUDIO_UNKNOWN ||
     311           0 :         ty->tivo_type == TIVO_TYPE_UNKNOWN)
     312           0 :         return AVERROR(EIO);
     313             : 
     314           0 :     st = avformat_new_stream(s, NULL);
     315           0 :     if (!st)
     316           0 :         return AVERROR(ENOMEM);
     317           0 :     st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
     318           0 :     st->codecpar->codec_id   = AV_CODEC_ID_MPEG2VIDEO;
     319           0 :     st->need_parsing         = AVSTREAM_PARSE_FULL_RAW;
     320           0 :     avpriv_set_pts_info(st, 64, 1, 90000);
     321             : 
     322           0 :     ast = avformat_new_stream(s, NULL);
     323           0 :     if (!ast)
     324           0 :         return AVERROR(ENOMEM);
     325           0 :     ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
     326             : 
     327           0 :     if (ty->audio_type == TIVO_AUDIO_MPEG) {
     328           0 :         ast->codecpar->codec_id = AV_CODEC_ID_MP2;
     329           0 :         ast->need_parsing       = AVSTREAM_PARSE_FULL_RAW;
     330             :     } else {
     331           0 :         ast->codecpar->codec_id = AV_CODEC_ID_AC3;
     332             :     }
     333           0 :     avpriv_set_pts_info(ast, 64, 1, 90000);
     334             : 
     335           0 :     ty->first_chunk = 1;
     336             : 
     337           0 :     avio_seek(pb, 0, SEEK_SET);
     338             : 
     339           0 :     return 0;
     340             : }
     341             : 
     342             : /* parse a master chunk, filling the SEQ table and other variables.
     343             :  * We assume the stream is currently pointing to it.
     344             :  */
     345           0 : static void parse_master(AVFormatContext *s)
     346             : {
     347           0 :     TYDemuxContext *ty = s->priv_data;
     348             :     unsigned map_size;  /* size of bitmask, in bytes */
     349             :     unsigned i, j;
     350             : 
     351             :     /* Note that the entries in the SEQ table in the stream may have
     352             :        different sizes depending on the bits per entry.  We store them
     353             :        all in the same size structure, so we have to parse them out one
     354             :        by one.  If we had a dynamic structure, we could simply read the
     355             :        entire table directly from the stream into memory in place. */
     356             : 
     357             :     /* clear the SEQ table */
     358           0 :     av_freep(&ty->seq_table);
     359             : 
     360             :     /* parse header info */
     361             : 
     362           0 :     map_size = AV_RB32(ty->chunk + 20);  /* size of bitmask, in bytes */
     363           0 :     i = AV_RB32(ty->chunk + 28);   /* size of SEQ table, in bytes */
     364             : 
     365           0 :     ty->seq_table_size = i / (8LL + map_size);
     366             : 
     367           0 :     if (ty->seq_table_size == 0) {
     368           0 :         ty->seq_table = NULL;
     369           0 :         return;
     370             :     }
     371             : 
     372             :     /* parse all the entries */
     373           0 :     ty->seq_table = av_calloc(ty->seq_table_size, sizeof(TySeqTable));
     374           0 :     if (ty->seq_table == NULL) {
     375           0 :         ty->seq_table_size = 0;
     376           0 :         return;
     377             :     }
     378             : 
     379           0 :     ty->cur_chunk_pos = 32;
     380           0 :     for (j = 0; j < ty->seq_table_size; j++) {
     381           0 :         if (ty->cur_chunk_pos >= CHUNK_SIZE - 8)
     382           0 :             return;
     383           0 :         ty->seq_table[j].timestamp = AV_RB64(ty->chunk + ty->cur_chunk_pos);
     384           0 :         ty->cur_chunk_pos += 8;
     385           0 :         if (map_size > 8) {
     386           0 :             av_log(s, AV_LOG_ERROR, "Unsupported SEQ bitmap size in master chunk.\n");
     387           0 :             ty->cur_chunk_pos += map_size;
     388             :         } else {
     389           0 :             memcpy(ty->seq_table[j].chunk_bitmask, ty->chunk + ty->cur_chunk_pos, map_size);
     390             :         }
     391             :     }
     392             : }
     393             : 
     394           0 : static int get_chunk(AVFormatContext *s)
     395             : {
     396           0 :     TYDemuxContext *ty = s->priv_data;
     397           0 :     AVIOContext *pb = s->pb;
     398             :     int read_size, num_recs;
     399             : 
     400             :     ff_dlog(s, "parsing ty chunk #%d\n", ty->cur_chunk);
     401             : 
     402             :     /* if we have left-over filler space from the last chunk, get that */
     403           0 :     if (avio_feof(pb))
     404           0 :         return AVERROR_EOF;
     405             : 
     406             :     /* read the TY packet header */
     407           0 :     read_size = avio_read(pb, ty->chunk, CHUNK_SIZE);
     408           0 :     ty->cur_chunk++;
     409             : 
     410           0 :     if ((read_size < 4) || (AV_RB32(ty->chunk) == 0)) {
     411           0 :         return AVERROR_EOF;
     412             :     }
     413             : 
     414             :     /* check if it's a PART Header */
     415           0 :     if (AV_RB32(ty->chunk) == TIVO_PES_FILEID) {
     416           0 :         parse_master(s); /* parse master chunk */
     417           0 :         return get_chunk(s);
     418             :     }
     419             : 
     420             :     /* number of records in chunk (8- or 16-bit number) */
     421           0 :     if (ty->chunk[3] & 0x80) {
     422             :         /* 16 bit rec cnt */
     423           0 :         ty->num_recs = num_recs = (ty->chunk[1] << 8) + ty->chunk[0];
     424           0 :         ty->seq_rec = (ty->chunk[3] << 8) + ty->chunk[2];
     425           0 :         if (ty->seq_rec != 0xffff) {
     426           0 :             ty->seq_rec &= ~0x8000;
     427             :         }
     428             :     } else {
     429             :         /* 8 bit reclen - TiVo 1.3 format */
     430           0 :         ty->num_recs = num_recs = ty->chunk[0];
     431           0 :         ty->seq_rec = ty->chunk[1];
     432             :     }
     433           0 :     ty->cur_rec = 0;
     434           0 :     ty->first_chunk = 0;
     435             : 
     436             :     ff_dlog(s, "chunk has %d records\n", num_recs);
     437           0 :     ty->cur_chunk_pos = 4;
     438             : 
     439           0 :     av_freep(&ty->rec_hdrs);
     440             : 
     441           0 :     if (num_recs * 16 >= CHUNK_SIZE - 4)
     442           0 :         return AVERROR_INVALIDDATA;
     443             : 
     444           0 :     ty->rec_hdrs = parse_chunk_headers(ty->chunk + 4, num_recs);
     445           0 :     if (!ty->rec_hdrs)
     446           0 :         return AVERROR(ENOMEM);
     447           0 :     ty->cur_chunk_pos += 16 * num_recs;
     448             : 
     449           0 :     return 0;
     450             : }
     451             : 
     452           0 : static int demux_video(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
     453             : {
     454           0 :     TYDemuxContext *ty = s->priv_data;
     455           0 :     const int subrec_type = rec_hdr->subrec_type;
     456           0 :     const int64_t rec_size = rec_hdr->rec_size;
     457             :     int es_offset1;
     458           0 :     int got_packet = 0;
     459             : 
     460           0 :     if (subrec_type != 0x02 && subrec_type != 0x0c &&
     461           0 :         subrec_type != 0x08 && rec_size > 4) {
     462             :         /* get the PTS from this packet if it has one.
     463             :          * on S1, only 0x06 has PES.  On S2, however, most all do.
     464             :          * Do NOT Pass the PES Header to the MPEG2 codec */
     465           0 :         es_offset1 = find_es_header(ty_VideoPacket, ty->chunk + ty->cur_chunk_pos, 5);
     466           0 :         if (es_offset1 != -1) {
     467           0 :             ty->last_video_pts = ff_parse_pes_pts(
     468           0 :                     ty->chunk + ty->cur_chunk_pos + es_offset1 + VIDEO_PTS_OFFSET);
     469           0 :             if (subrec_type != 0x06) {
     470             :                 /* if we found a PES, and it's not type 6, then we're S2 */
     471             :                 /* The packet will have video data (& other headers) so we
     472             :                  * chop out the PES header and send the rest */
     473           0 :                 if (rec_size >= VIDEO_PES_LENGTH + es_offset1) {
     474           0 :                     int size = rec_hdr->rec_size - VIDEO_PES_LENGTH - es_offset1;
     475             : 
     476           0 :                     ty->cur_chunk_pos += VIDEO_PES_LENGTH + es_offset1;
     477           0 :                     if (av_new_packet(pkt, size) < 0)
     478           0 :                         return AVERROR(ENOMEM);
     479           0 :                     memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, size);
     480           0 :                     ty->cur_chunk_pos += size;
     481           0 :                     pkt->stream_index = 0;
     482           0 :                     got_packet = 1;
     483             :                 } else {
     484             :                     ff_dlog(s, "video rec type 0x%02x has short PES"
     485             :                         " (%"PRId64" bytes)\n", subrec_type, rec_size);
     486             :                     /* nuke this block; it's too short, but has PES marker */
     487           0 :                     ty->cur_chunk_pos += rec_size;
     488           0 :                     return 0;
     489             :                 }
     490             :             }
     491             :         }
     492             :     }
     493             : 
     494           0 :     if (subrec_type == 0x06) {
     495             :         /* type 6 (S1 DTivo) has no data, so we're done */
     496           0 :         ty->cur_chunk_pos += rec_size;
     497           0 :         return 0;
     498             :     }
     499             : 
     500           0 :     if (!got_packet) {
     501           0 :         if (av_new_packet(pkt, rec_size) < 0)
     502           0 :             return AVERROR(ENOMEM);
     503           0 :         memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
     504           0 :         ty->cur_chunk_pos += rec_size;
     505           0 :         pkt->stream_index = 0;
     506           0 :         got_packet = 1;
     507             :     }
     508             : 
     509             :     /* if it's not a continue blk, then set PTS */
     510           0 :     if (subrec_type != 0x02) {
     511           0 :         if (subrec_type == 0x0c && pkt->size >= 6)
     512           0 :             pkt->data[5] |= 0x08;
     513           0 :         if (subrec_type == 0x07) {
     514           0 :             ty->last_ty_pts = rec_hdr->ty_pts;
     515             :         } else {
     516             :             /* yes I know this is a cheap hack.  It's the timestamp
     517             :                used for display and skipping fwd/back, so it
     518             :                doesn't have to be accurate to the millisecond.
     519             :                I adjust it here by roughly one 1/30 sec.  Yes it
     520             :                will be slightly off for UK streams, but it's OK.
     521             :              */
     522           0 :             ty->last_ty_pts += 35000000;
     523             :             //ty->last_ty_pts += 33366667;
     524             :         }
     525             :         /* set PTS for this block before we send */
     526           0 :         if (ty->last_video_pts > AV_NOPTS_VALUE) {
     527           0 :             pkt->pts = ty->last_video_pts;
     528             :             /* PTS gets used ONCE.
     529             :              * Any subsequent frames we get BEFORE next PES
     530             :              * header will have their PTS computed in the codec */
     531           0 :             ty->last_video_pts = AV_NOPTS_VALUE;
     532             :         }
     533             :     }
     534             : 
     535           0 :     return got_packet;
     536             : }
     537             : 
     538           0 : static int check_sync_pes(AVFormatContext *s, AVPacket *pkt,
     539             :                           int32_t offset, int32_t rec_len)
     540             : {
     541           0 :     TYDemuxContext *ty = s->priv_data;
     542             : 
     543           0 :     if (offset < 0 || offset + ty->pes_length > rec_len) {
     544             :         /* entire PES header not present */
     545             :         ff_dlog(s, "PES header at %"PRId32" not complete in record. storing.\n", offset);
     546             :         /* save the partial pes header */
     547           0 :         if (offset < 0) {
     548             :             /* no header found, fake some 00's (this works, believe me) */
     549           0 :             memset(ty->pes_buffer, 0, 4);
     550           0 :             ty->pes_buf_cnt = 4;
     551             :             if (rec_len > 4)
     552             :                 ff_dlog(s, "PES header not found in record of %"PRId32" bytes!\n", rec_len);
     553           0 :             return -1;
     554             :         }
     555             :         /* copy the partial pes header we found */
     556           0 :         memcpy(ty->pes_buffer, pkt->data + offset, rec_len - offset);
     557           0 :         ty->pes_buf_cnt = rec_len - offset;
     558             : 
     559           0 :         if (offset > 0) {
     560             :             /* PES Header was found, but not complete, so trim the end of this record */
     561           0 :             pkt->size -= rec_len - offset;
     562           0 :             return 1;
     563             :         }
     564           0 :         return -1;    /* partial PES, no audio data */
     565             :     }
     566             :     /* full PES header present, extract PTS */
     567           0 :     ty->last_audio_pts = ff_parse_pes_pts(&pkt->data[ offset + ty->pts_offset]);
     568           0 :     if (ty->first_audio_pts == AV_NOPTS_VALUE)
     569           0 :         ty->first_audio_pts = ty->last_audio_pts;
     570           0 :     pkt->pts = ty->last_audio_pts;
     571           0 :     memmove(pkt->data + offset, pkt->data + offset + ty->pes_length, rec_len - ty->pes_length);
     572           0 :     pkt->size -= ty->pes_length;
     573           0 :     return 0;
     574             : }
     575             : 
     576           0 : static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
     577             : {
     578           0 :     TYDemuxContext *ty = s->priv_data;
     579           0 :     const int subrec_type = rec_hdr->subrec_type;
     580           0 :     const int64_t rec_size = rec_hdr->rec_size;
     581             :     int es_offset1;
     582             : 
     583           0 :     if (subrec_type == 2) {
     584           0 :         int need = 0;
     585             :         /* SA or DTiVo Audio Data, no PES (continued block)
     586             :          * ================================================
     587             :          */
     588             : 
     589             :         /* continue PES if previous was incomplete */
     590           0 :         if (ty->pes_buf_cnt > 0) {
     591           0 :             need = ty->pes_length - ty->pes_buf_cnt;
     592             : 
     593             :             ff_dlog(s, "continuing PES header\n");
     594             :             /* do we have enough data to complete? */
     595           0 :             if (need >= rec_size) {
     596             :                 /* don't have complete PES hdr; save what we have and return */
     597           0 :                 memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, rec_size);
     598           0 :                 ty->cur_chunk_pos += rec_size;
     599           0 :                 ty->pes_buf_cnt += rec_size;
     600           0 :                 return 0;
     601             :             }
     602             : 
     603             :             /* we have enough; reconstruct this frame with the new hdr */
     604           0 :             memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, need);
     605           0 :             ty->cur_chunk_pos += need;
     606             :             /* get the PTS out of this PES header (MPEG or AC3) */
     607           0 :             if (ty->audio_type == TIVO_AUDIO_MPEG) {
     608           0 :                 es_offset1 = find_es_header(ty_MPEGAudioPacket,
     609           0 :                         ty->pes_buffer, 5);
     610             :             } else {
     611           0 :                 es_offset1 = find_es_header(ty_AC3AudioPacket,
     612           0 :                         ty->pes_buffer, 5);
     613             :             }
     614           0 :             if (es_offset1 < 0) {
     615             :                 ff_dlog(s, "Can't find audio PES header in packet.\n");
     616             :             } else {
     617           0 :                 ty->last_audio_pts = ff_parse_pes_pts(
     618           0 :                     &ty->pes_buffer[es_offset1 + ty->pts_offset]);
     619           0 :                 pkt->pts = ty->last_audio_pts;
     620             :             }
     621           0 :             ty->pes_buf_cnt = 0;
     622             : 
     623             :         }
     624           0 :         if (av_new_packet(pkt, rec_size - need) < 0)
     625           0 :             return AVERROR(ENOMEM);
     626           0 :         memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size - need);
     627           0 :         ty->cur_chunk_pos += rec_size - need;
     628           0 :         pkt->stream_index = 1;
     629             : 
     630             :         /* S2 DTivo has AC3 packets with 2 padding bytes at end.  This is
     631             :          * not allowed in the AC3 spec and will cause problems.  So here
     632             :          * we try to trim things. */
     633             :         /* Also, S1 DTivo has alternating short / long AC3 packets.  That
     634             :          * is, one packet is short (incomplete) and the next packet has
     635             :          * the first one's missing data, plus all of its own.  Strange. */
     636           0 :         if (ty->audio_type == TIVO_AUDIO_AC3 &&
     637           0 :                 ty->tivo_series == TIVO_SERIES2) {
     638           0 :             if (ty->ac3_pkt_size + pkt->size > AC3_PKT_LENGTH) {
     639           0 :                 pkt->size -= 2;
     640           0 :                 ty->ac3_pkt_size = 0;
     641             :             } else {
     642           0 :                 ty->ac3_pkt_size += pkt->size;
     643             :             }
     644             :         }
     645           0 :     } else if (subrec_type == 0x03) {
     646           0 :         if (av_new_packet(pkt, rec_size) < 0)
     647           0 :             return AVERROR(ENOMEM);
     648           0 :         memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
     649           0 :         ty->cur_chunk_pos += rec_size;
     650           0 :         pkt->stream_index = 1;
     651             :         /* MPEG Audio with PES Header, either SA or DTiVo   */
     652             :         /* ================================================ */
     653           0 :         es_offset1 = find_es_header(ty_MPEGAudioPacket, pkt->data, 5);
     654             : 
     655             :         /* SA PES Header, No Audio Data                     */
     656             :         /* ================================================ */
     657           0 :         if ((es_offset1 == 0) && (rec_size == 16)) {
     658           0 :             ty->last_audio_pts = ff_parse_pes_pts(&pkt->data[SA_PTS_OFFSET]);
     659           0 :             if (ty->first_audio_pts == AV_NOPTS_VALUE)
     660           0 :                 ty->first_audio_pts = ty->last_audio_pts;
     661           0 :             av_packet_unref(pkt);
     662           0 :             return 0;
     663             :         }
     664             :         /* DTiVo Audio with PES Header                      */
     665             :         /* ================================================ */
     666             : 
     667             :         /* Check for complete PES */
     668           0 :         if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
     669             :             /* partial PES header found, nothing else.
     670             :              * we're done. */
     671           0 :             av_packet_unref(pkt);
     672           0 :             return 0;
     673             :         }
     674           0 :     } else if (subrec_type == 0x04) {
     675             :         /* SA Audio with no PES Header                      */
     676             :         /* ================================================ */
     677           0 :         if (av_new_packet(pkt, rec_size) < 0)
     678           0 :             return AVERROR(ENOMEM);
     679           0 :         memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
     680           0 :         ty->cur_chunk_pos += rec_size;
     681           0 :         pkt->stream_index = 1;
     682           0 :         pkt->pts = ty->last_audio_pts;
     683           0 :     } else if (subrec_type == 0x09) {
     684           0 :         if (av_new_packet(pkt, rec_size) < 0)
     685           0 :             return AVERROR(ENOMEM);
     686           0 :         memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
     687           0 :         ty->cur_chunk_pos += rec_size ;
     688           0 :         pkt->stream_index = 1;
     689             : 
     690             :         /* DTiVo AC3 Audio Data with PES Header             */
     691             :         /* ================================================ */
     692           0 :         es_offset1 = find_es_header(ty_AC3AudioPacket, pkt->data, 5);
     693             : 
     694             :         /* Check for complete PES */
     695           0 :         if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
     696             :             /* partial PES header found, nothing else.  we're done. */
     697           0 :             av_packet_unref(pkt);
     698           0 :             return 0;
     699             :         }
     700             :         /* S2 DTivo has invalid long AC3 packets */
     701           0 :         if (ty->tivo_series == TIVO_SERIES2) {
     702           0 :             if (pkt->size > AC3_PKT_LENGTH) {
     703           0 :                 pkt->size -= 2;
     704           0 :                 ty->ac3_pkt_size = 0;
     705             :             } else {
     706           0 :                 ty->ac3_pkt_size = pkt->size;
     707             :             }
     708             :         }
     709             :     } else {
     710             :         /* Unsupported/Unknown */
     711           0 :         ty->cur_chunk_pos += rec_size;
     712           0 :         return 0;
     713             :     }
     714             : 
     715           0 :     return 1;
     716             : }
     717             : 
     718           0 : static int ty_read_packet(AVFormatContext *s, AVPacket *pkt)
     719             : {
     720           0 :     TYDemuxContext *ty = s->priv_data;
     721           0 :     AVIOContext *pb = s->pb;
     722             :     TyRecHdr *rec;
     723           0 :     int64_t rec_size = 0;
     724           0 :     int ret = 0;
     725             : 
     726           0 :     if (avio_feof(pb))
     727           0 :         return AVERROR_EOF;
     728             : 
     729           0 :     while (ret <= 0) {
     730           0 :         if (!ty->rec_hdrs || ty->first_chunk || ty->cur_rec >= ty->num_recs) {
     731           0 :             if (get_chunk(s) < 0 || ty->num_recs <= 0)
     732           0 :                 return AVERROR_EOF;
     733             :         }
     734             : 
     735           0 :         rec = &ty->rec_hdrs[ty->cur_rec];
     736           0 :         rec_size = rec->rec_size;
     737           0 :         ty->cur_rec++;
     738             : 
     739           0 :         if (rec_size <= 0)
     740           0 :             continue;
     741             : 
     742           0 :         if (ty->cur_chunk_pos + rec->rec_size > CHUNK_SIZE)
     743           0 :             return AVERROR_INVALIDDATA;
     744             : 
     745           0 :         if (avio_feof(pb))
     746           0 :             return AVERROR_EOF;
     747             : 
     748           0 :         switch (rec->rec_type) {
     749           0 :         case VIDEO_ID:
     750           0 :             ret = demux_video(s, rec, pkt);
     751           0 :             break;
     752           0 :         case AUDIO_ID:
     753           0 :             ret = demux_audio(s, rec, pkt);
     754           0 :             break;
     755           0 :         default:
     756             :             ff_dlog(s, "Invalid record type 0x%02x\n", rec->rec_type);
     757             :         case 0x01:
     758             :         case 0x02:
     759             :         case 0x03: /* TiVo data services */
     760             :         case 0x05: /* unknown, but seen regularly */
     761           0 :             ty->cur_chunk_pos += rec->rec_size;
     762           0 :             break;
     763             :         }
     764             :     }
     765             : 
     766           0 :     return 0;
     767             : }
     768             : 
     769           0 : static int ty_read_close(AVFormatContext *s)
     770             : {
     771           0 :     TYDemuxContext *ty = s->priv_data;
     772             : 
     773           0 :     av_freep(&ty->seq_table);
     774           0 :     av_freep(&ty->rec_hdrs);
     775             : 
     776           0 :     return 0;
     777             : }
     778             : 
     779             : AVInputFormat ff_ty_demuxer = {
     780             :     .name           = "ty",
     781             :     .long_name      = NULL_IF_CONFIG_SMALL("TiVo TY Stream"),
     782             :     .priv_data_size = sizeof(TYDemuxContext),
     783             :     .read_probe     = ty_probe,
     784             :     .read_header    = ty_read_header,
     785             :     .read_packet    = ty_read_packet,
     786             :     .read_close     = ty_read_close,
     787             :     .extensions     = "ty,ty+",
     788             :     .flags          = AVFMT_TS_DISCONT,
     789             : };

Generated by: LCOV version 1.13