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