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