FFmpeg  4.4.4
ty.c
Go to the documentation of this file.
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];
54  uint64_t ty_pts; /* TY PTS in the record header */
55 } TyRecHdr;
56 
57 typedef enum {
62 
63 typedef enum {
68 
69 typedef enum {
74 
75 typedef struct TYDemuxContext {
76  unsigned cur_chunk;
77  unsigned cur_chunk_pos;
78  int64_t cur_pos;
79  TiVo_type tivo_type; /* TiVo type (SA / DTiVo) */
80  TiVo_series tivo_series; /* Series1 or Series2 */
81  TiVo_audio audio_type; /* AC3 or MPEG */
82  int pes_length; /* Length of Audio PES header */
83  int pts_offset; /* offset into audio PES of PTS */
84  uint8_t pes_buffer[20]; /* holds incomplete pes headers */
85  int pes_buf_cnt; /* how many bytes in our buffer */
86  size_t ac3_pkt_size; /* length of ac3 pkt we've seen so far */
87  uint64_t last_ty_pts; /* last TY timestamp we've seen */
88 
89  int64_t first_audio_pts;
90  int64_t last_audio_pts;
91  int64_t last_video_pts;
92 
93  TyRecHdr *rec_hdrs; /* record headers array */
94  int cur_rec; /* current record in this chunk */
95  int num_recs; /* number of recs in this chunk */
97 
100 
101 static int ty_probe(const AVProbeData *p)
102 {
103  int i;
104 
105  for (i = 0; i + 12 < p->buf_size; i += CHUNK_SIZE) {
106  if (AV_RB32(p->buf + i) == TIVO_PES_FILEID &&
107  AV_RB32(p->buf + i + 4) == 0x02 &&
108  AV_RB32(p->buf + i + 8) == CHUNK_SIZE) {
109  return AVPROBE_SCORE_MAX;
110  }
111  }
112 
113  return 0;
114 }
115 
117  int num_recs)
118 {
119  TyRecHdr *hdrs, *rec_hdr;
120  int i;
121 
122  hdrs = av_calloc(num_recs, sizeof(TyRecHdr));
123  if (!hdrs)
124  return NULL;
125 
126  for (i = 0; i < num_recs; i++) {
127  const uint8_t *record_header = buf + (i * 16);
128 
129  rec_hdr = &hdrs[i]; /* for brevity */
130  rec_hdr->rec_type = record_header[3];
131  rec_hdr->subrec_type = record_header[2] & 0x0f;
132  if ((record_header[0] & 0x80) == 0x80) {
133  uint8_t b1, b2;
134 
135  /* marker bit 2 set, so read extended data */
136  b1 = (((record_header[0] & 0x0f) << 4) |
137  ((record_header[1] & 0xf0) >> 4));
138  b2 = (((record_header[1] & 0x0f) << 4) |
139  ((record_header[2] & 0xf0) >> 4));
140 
141  rec_hdr->ex[0] = b1;
142  rec_hdr->ex[1] = b2;
143  rec_hdr->rec_size = 0;
144  rec_hdr->ty_pts = 0;
145  } else {
146  rec_hdr->rec_size = (record_header[0] << 8 |
147  record_header[1]) << 4 |
148  (record_header[2] >> 4);
149  rec_hdr->ty_pts = AV_RB64(&record_header[8]);
150  }
151  }
152  return hdrs;
153 }
154 
155 static int find_es_header(const uint8_t *header,
156  const uint8_t *buffer, int search_len)
157 {
158  int count;
159 
160  for (count = 0; count < search_len; count++) {
161  if (!memcmp(&buffer[count], header, 4))
162  return count;
163  }
164  return -1;
165 }
166 
167 static int analyze_chunk(AVFormatContext *s, const uint8_t *chunk)
168 {
169  TYDemuxContext *ty = s->priv_data;
170  int num_recs, i;
171  TyRecHdr *hdrs;
172  int num_6e0, num_be0, num_9c0, num_3c0;
173 
174  /* skip if it's a Part header */
175  if (AV_RB32(&chunk[0]) == TIVO_PES_FILEID)
176  return 0;
177 
178  /* number of records in chunk (we ignore high order byte;
179  * rarely are there > 256 chunks & we don't need that many anyway) */
180  num_recs = chunk[0];
181  if (num_recs < 5) {
182  /* try again with the next chunk. Sometimes there are dead ones */
183  return 0;
184  }
185 
186  chunk += 4; /* skip past rec count & SEQ bytes */
187  ff_dlog(s, "probe: chunk has %d recs\n", num_recs);
188  hdrs = parse_chunk_headers(chunk, num_recs);
189  if (!hdrs)
190  return AVERROR(ENOMEM);
191 
192  /* scan headers.
193  * 1. check video packets. Presence of 0x6e0 means S1.
194  * No 6e0 but have be0 means S2.
195  * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
196  * If AC-3, then we have DTivo.
197  * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
198  */
199  num_6e0 = num_be0 = num_9c0 = num_3c0 = 0;
200  for (i = 0; i < num_recs; i++) {
201  switch (hdrs[i].subrec_type << 8 | hdrs[i].rec_type) {
202  case 0x6e0:
203  num_6e0++;
204  break;
205  case 0xbe0:
206  num_be0++;
207  break;
208  case 0x3c0:
209  num_3c0++;
210  break;
211  case 0x9c0:
212  num_9c0++;
213  break;
214  }
215  }
216  ff_dlog(s, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.\n",
217  num_6e0, num_be0);
218 
219  /* set up our variables */
220  if (num_6e0 > 0) {
221  ff_dlog(s, "detected Series 1 Tivo\n");
224  } else if (num_be0 > 0) {
225  ff_dlog(s, "detected Series 2 Tivo\n");
228  }
229  if (num_9c0 > 0) {
230  ff_dlog(s, "detected AC-3 Audio (DTivo)\n");
235  } else if (num_3c0 > 0) {
237  ff_dlog(s, "detected MPEG Audio\n");
238  }
239 
240  /* if tivo_type still unknown, we can check PTS location
241  * in MPEG packets to determine tivo_type */
242  if (ty->tivo_type == TIVO_TYPE_UNKNOWN) {
243  uint32_t data_offset = 16 * num_recs;
244 
245  for (i = 0; i < num_recs; i++) {
246  if (data_offset + hdrs[i].rec_size > CHUNK_SIZE)
247  break;
248 
249  if ((hdrs[i].subrec_type << 8 | hdrs[i].rec_type) == 0x3c0 && hdrs[i].rec_size > 15) {
250  /* first make sure we're aligned */
251  int pes_offset = find_es_header(ty_MPEGAudioPacket,
252  &chunk[data_offset], 5);
253  if (pes_offset >= 0) {
254  /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
255  if ((chunk[data_offset + 6 + pes_offset] & 0x80) == 0x80) {
256  /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
257  if (ty->tivo_series == TIVO_SERIES1)
258  ff_dlog(s, "detected Stand-Alone Tivo\n");
259  ty->tivo_type = TIVO_TYPE_SA;
261  } else {
262  if (ty->tivo_series == TIVO_SERIES1)
263  ff_dlog(s, "detected DirecTV Tivo\n");
266  }
267  break;
268  }
269  }
270  data_offset += hdrs[i].rec_size;
271  }
272  }
273  av_free(hdrs);
274 
275  return 0;
276 }
277 
279 {
280  TYDemuxContext *ty = s->priv_data;
281  AVIOContext *pb = s->pb;
282  AVStream *st, *ast;
283  int i, ret = 0;
284 
288 
289  for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
290  avio_read(pb, ty->chunk, CHUNK_SIZE);
291 
292  ret = analyze_chunk(s, ty->chunk);
293  if (ret < 0)
294  return ret;
295  if (ty->tivo_series != TIVO_SERIES_UNKNOWN &&
298  break;
299  }
300 
301  if (ty->tivo_series == TIVO_SERIES_UNKNOWN ||
304  return AVERROR(EIO);
305 
306  st = avformat_new_stream(s, NULL);
307  if (!st)
308  return AVERROR(ENOMEM);
312  avpriv_set_pts_info(st, 64, 1, 90000);
313 
314  ast = avformat_new_stream(s, NULL);
315  if (!ast)
316  return AVERROR(ENOMEM);
318 
319  if (ty->audio_type == TIVO_AUDIO_MPEG) {
322  } else {
324  }
325  avpriv_set_pts_info(ast, 64, 1, 90000);
326 
327  ty->first_chunk = 1;
328 
329  avio_seek(pb, 0, SEEK_SET);
330 
331  return 0;
332 }
333 
335 {
336  TYDemuxContext *ty = s->priv_data;
337  AVIOContext *pb = s->pb;
338  int read_size, num_recs;
339 
340  ff_dlog(s, "parsing ty chunk #%d\n", ty->cur_chunk);
341 
342  /* if we have left-over filler space from the last chunk, get that */
343  if (avio_feof(pb))
344  return AVERROR_EOF;
345 
346  /* read the TY packet header */
347  read_size = avio_read(pb, ty->chunk, CHUNK_SIZE);
348  ty->cur_chunk++;
349 
350  if ((read_size < 4) || (AV_RB32(ty->chunk) == 0)) {
351  return AVERROR_EOF;
352  }
353 
354  /* check if it's a PART Header */
355  if (AV_RB32(ty->chunk) == TIVO_PES_FILEID) {
356  /* skip master chunk and read new chunk */
357  return get_chunk(s);
358  }
359 
360  /* number of records in chunk (8- or 16-bit number) */
361  if (ty->chunk[3] & 0x80) {
362  /* 16 bit rec cnt */
363  ty->num_recs = num_recs = (ty->chunk[1] << 8) + ty->chunk[0];
364  } else {
365  /* 8 bit reclen - TiVo 1.3 format */
366  ty->num_recs = num_recs = ty->chunk[0];
367  }
368  ty->cur_rec = 0;
369  ty->first_chunk = 0;
370 
371  ff_dlog(s, "chunk has %d records\n", num_recs);
372  ty->cur_chunk_pos = 4;
373 
374  av_freep(&ty->rec_hdrs);
375 
376  if (num_recs * 16 >= CHUNK_SIZE - 4)
377  return AVERROR_INVALIDDATA;
378 
379  ty->rec_hdrs = parse_chunk_headers(ty->chunk + 4, num_recs);
380  if (!ty->rec_hdrs)
381  return AVERROR(ENOMEM);
382  ty->cur_chunk_pos += 16 * num_recs;
383 
384  return 0;
385 }
386 
388 {
389  TYDemuxContext *ty = s->priv_data;
390  const int subrec_type = rec_hdr->subrec_type;
391  const int64_t rec_size = rec_hdr->rec_size;
392  int es_offset1, ret;
393  int got_packet = 0;
394 
395  if (subrec_type != 0x02 && subrec_type != 0x0c &&
396  subrec_type != 0x08 && rec_size > 4) {
397  /* get the PTS from this packet if it has one.
398  * on S1, only 0x06 has PES. On S2, however, most all do.
399  * Do NOT Pass the PES Header to the MPEG2 codec */
400  es_offset1 = find_es_header(ty_VideoPacket, ty->chunk + ty->cur_chunk_pos, 5);
401  if (es_offset1 != -1) {
403  ty->chunk + ty->cur_chunk_pos + es_offset1 + VIDEO_PTS_OFFSET);
404  if (subrec_type != 0x06) {
405  /* if we found a PES, and it's not type 6, then we're S2 */
406  /* The packet will have video data (& other headers) so we
407  * chop out the PES header and send the rest */
408  if (rec_size >= VIDEO_PES_LENGTH + es_offset1) {
409  int size = rec_hdr->rec_size - VIDEO_PES_LENGTH - es_offset1;
410 
411  ty->cur_chunk_pos += VIDEO_PES_LENGTH + es_offset1;
412  if ((ret = av_new_packet(pkt, size)) < 0)
413  return ret;
414  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, size);
415  ty->cur_chunk_pos += size;
416  pkt->stream_index = 0;
417  got_packet = 1;
418  } else {
419  ff_dlog(s, "video rec type 0x%02x has short PES"
420  " (%"PRId64" bytes)\n", subrec_type, rec_size);
421  /* nuke this block; it's too short, but has PES marker */
422  ty->cur_chunk_pos += rec_size;
423  return 0;
424  }
425  }
426  }
427  }
428 
429  if (subrec_type == 0x06) {
430  /* type 6 (S1 DTivo) has no data, so we're done */
431  ty->cur_chunk_pos += rec_size;
432  return 0;
433  }
434 
435  if (!got_packet) {
436  if ((ret = av_new_packet(pkt, rec_size)) < 0)
437  return ret;
438  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
439  ty->cur_chunk_pos += rec_size;
440  pkt->stream_index = 0;
441  got_packet = 1;
442  }
443 
444  /* if it's not a continue blk, then set PTS */
445  if (subrec_type != 0x02) {
446  if (subrec_type == 0x0c && pkt->size >= 6)
447  pkt->data[5] |= 0x08;
448  if (subrec_type == 0x07) {
449  ty->last_ty_pts = rec_hdr->ty_pts;
450  } else {
451  /* yes I know this is a cheap hack. It's the timestamp
452  used for display and skipping fwd/back, so it
453  doesn't have to be accurate to the millisecond.
454  I adjust it here by roughly one 1/30 sec. Yes it
455  will be slightly off for UK streams, but it's OK.
456  */
457  ty->last_ty_pts += 35000000;
458  //ty->last_ty_pts += 33366667;
459  }
460  /* set PTS for this block before we send */
461  if (ty->last_video_pts > AV_NOPTS_VALUE) {
462  pkt->pts = ty->last_video_pts;
463  /* PTS gets used ONCE.
464  * Any subsequent frames we get BEFORE next PES
465  * header will have their PTS computed in the codec */
467  }
468  }
469 
470  return got_packet;
471 }
472 
474  int32_t offset, int32_t rec_len)
475 {
476  TYDemuxContext *ty = s->priv_data;
477 
478  if (offset < 0 || offset + ty->pes_length > rec_len) {
479  /* entire PES header not present */
480  ff_dlog(s, "PES header at %"PRId32" not complete in record. storing.\n", offset);
481  /* save the partial pes header */
482  if (offset < 0) {
483  /* no header found, fake some 00's (this works, believe me) */
484  memset(ty->pes_buffer, 0, 4);
485  ty->pes_buf_cnt = 4;
486  if (rec_len > 4)
487  ff_dlog(s, "PES header not found in record of %"PRId32" bytes!\n", rec_len);
488  return -1;
489  }
490  /* copy the partial pes header we found */
491  memcpy(ty->pes_buffer, pkt->data + offset, rec_len - offset);
492  ty->pes_buf_cnt = rec_len - offset;
493 
494  if (offset > 0) {
495  /* PES Header was found, but not complete, so trim the end of this record */
496  pkt->size -= rec_len - offset;
497  return 1;
498  }
499  return -1; /* partial PES, no audio data */
500  }
501  /* full PES header present, extract PTS */
503  if (ty->first_audio_pts == AV_NOPTS_VALUE)
505  pkt->pts = ty->last_audio_pts;
506  memmove(pkt->data + offset, pkt->data + offset + ty->pes_length, rec_len - ty->pes_length);
507  pkt->size -= ty->pes_length;
508  return 0;
509 }
510 
512 {
513  TYDemuxContext *ty = s->priv_data;
514  const int subrec_type = rec_hdr->subrec_type;
515  const int64_t rec_size = rec_hdr->rec_size;
516  int es_offset1, ret;
517 
518  if (subrec_type == 2) {
519  int need = 0;
520  /* SA or DTiVo Audio Data, no PES (continued block)
521  * ================================================
522  */
523 
524  /* continue PES if previous was incomplete */
525  if (ty->pes_buf_cnt > 0) {
526  need = ty->pes_length - ty->pes_buf_cnt;
527 
528  ff_dlog(s, "continuing PES header\n");
529  /* do we have enough data to complete? */
530  if (need >= rec_size) {
531  /* don't have complete PES hdr; save what we have and return */
532  memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, rec_size);
533  ty->cur_chunk_pos += rec_size;
534  ty->pes_buf_cnt += rec_size;
535  return 0;
536  }
537 
538  /* we have enough; reconstruct this frame with the new hdr */
539  memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, need);
540  ty->cur_chunk_pos += need;
541  /* get the PTS out of this PES header (MPEG or AC3) */
542  if (ty->audio_type == TIVO_AUDIO_MPEG) {
543  es_offset1 = find_es_header(ty_MPEGAudioPacket,
544  ty->pes_buffer, 5);
545  } else {
546  es_offset1 = find_es_header(ty_AC3AudioPacket,
547  ty->pes_buffer, 5);
548  }
549  if (es_offset1 < 0) {
550  ff_dlog(s, "Can't find audio PES header in packet.\n");
551  } else {
553  &ty->pes_buffer[es_offset1 + ty->pts_offset]);
554  pkt->pts = ty->last_audio_pts;
555  }
556  ty->pes_buf_cnt = 0;
557 
558  }
559  if ((ret = av_new_packet(pkt, rec_size - need)) < 0)
560  return ret;
561  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size - need);
562  ty->cur_chunk_pos += rec_size - need;
563  pkt->stream_index = 1;
564 
565  /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
566  * not allowed in the AC3 spec and will cause problems. So here
567  * we try to trim things. */
568  /* Also, S1 DTivo has alternating short / long AC3 packets. That
569  * is, one packet is short (incomplete) and the next packet has
570  * the first one's missing data, plus all of its own. Strange. */
571  if (ty->audio_type == TIVO_AUDIO_AC3 &&
572  ty->tivo_series == TIVO_SERIES2) {
573  if (ty->ac3_pkt_size + pkt->size > AC3_PKT_LENGTH) {
574  pkt->size -= 2;
575  ty->ac3_pkt_size = 0;
576  } else {
577  ty->ac3_pkt_size += pkt->size;
578  }
579  }
580  } else if (subrec_type == 0x03) {
581  if ((ret = av_new_packet(pkt, rec_size)) < 0)
582  return ret;
583  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
584  ty->cur_chunk_pos += rec_size;
585  pkt->stream_index = 1;
586  /* MPEG Audio with PES Header, either SA or DTiVo */
587  /* ================================================ */
588  es_offset1 = find_es_header(ty_MPEGAudioPacket, pkt->data, 5);
589 
590  /* SA PES Header, No Audio Data */
591  /* ================================================ */
592  if ((es_offset1 == 0) && (rec_size == 16)) {
594  if (ty->first_audio_pts == AV_NOPTS_VALUE)
597  return 0;
598  }
599  /* DTiVo Audio with PES Header */
600  /* ================================================ */
601 
602  /* Check for complete PES */
603  if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
604  /* partial PES header found, nothing else.
605  * we're done. */
607  return 0;
608  }
609  } else if (subrec_type == 0x04) {
610  /* SA Audio with no PES Header */
611  /* ================================================ */
612  if ((ret = av_new_packet(pkt, rec_size)) < 0)
613  return ret;
614  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
615  ty->cur_chunk_pos += rec_size;
616  pkt->stream_index = 1;
617  pkt->pts = ty->last_audio_pts;
618  } else if (subrec_type == 0x09) {
619  if ((ret = av_new_packet(pkt, rec_size)) < 0)
620  return ret;
621  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
622  ty->cur_chunk_pos += rec_size ;
623  pkt->stream_index = 1;
624 
625  /* DTiVo AC3 Audio Data with PES Header */
626  /* ================================================ */
627  es_offset1 = find_es_header(ty_AC3AudioPacket, pkt->data, 5);
628 
629  /* Check for complete PES */
630  if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
631  /* partial PES header found, nothing else. we're done. */
633  return 0;
634  }
635  /* S2 DTivo has invalid long AC3 packets */
636  if (ty->tivo_series == TIVO_SERIES2) {
637  if (pkt->size > AC3_PKT_LENGTH) {
638  pkt->size -= 2;
639  ty->ac3_pkt_size = 0;
640  } else {
641  ty->ac3_pkt_size = pkt->size;
642  }
643  }
644  } else {
645  /* Unsupported/Unknown */
646  ty->cur_chunk_pos += rec_size;
647  return 0;
648  }
649 
650  return 1;
651 }
652 
654 {
655  TYDemuxContext *ty = s->priv_data;
656  AVIOContext *pb = s->pb;
657  TyRecHdr *rec;
658  int64_t rec_size = 0;
659  int ret = 0;
660 
661  if (avio_feof(pb))
662  return AVERROR_EOF;
663 
664  while (ret <= 0) {
665  if (!ty->rec_hdrs || ty->first_chunk || ty->cur_rec >= ty->num_recs) {
666  if (get_chunk(s) < 0 || ty->num_recs <= 0)
667  return AVERROR_EOF;
668  }
669 
670  rec = &ty->rec_hdrs[ty->cur_rec];
671  rec_size = rec->rec_size;
672  ty->cur_rec++;
673 
674  if (rec_size <= 0)
675  continue;
676 
677  if (ty->cur_chunk_pos + rec->rec_size > CHUNK_SIZE)
678  return AVERROR_INVALIDDATA;
679 
680  if (avio_feof(pb))
681  return AVERROR_EOF;
682 
683  switch (rec->rec_type) {
684  case VIDEO_ID:
685  ret = demux_video(s, rec, pkt);
686  break;
687  case AUDIO_ID:
688  ret = demux_audio(s, rec, pkt);
689  break;
690  default:
691  ff_dlog(s, "Invalid record type 0x%02x\n", rec->rec_type);
692  case 0x01:
693  case 0x02:
694  case 0x03: /* TiVo data services */
695  case 0x05: /* unknown, but seen regularly */
696  ty->cur_chunk_pos += rec->rec_size;
697  break;
698  }
699  }
700 
701  return 0;
702 }
703 
705 {
706  TYDemuxContext *ty = s->priv_data;
707 
708  av_freep(&ty->rec_hdrs);
709 
710  return 0;
711 }
712 
714  .name = "ty",
715  .long_name = NULL_IF_CONFIG_SMALL("TiVo TY Stream"),
716  .priv_data_size = sizeof(TYDemuxContext),
717  .read_probe = ty_probe,
721  .extensions = "ty,ty+",
723 };
uint8_t
int32_t
Main libavformat public API header.
#define AVPROBE_SCORE_MAX
maximum score
Definition: avformat.h:453
#define AVFMT_TS_DISCONT
Format allows timestamp discontinuities.
Definition: avformat.h:464
@ AVSTREAM_PARSE_FULL_RAW
full parsing and repack with timestamp and position generation by parser for raw this assumes that ea...
Definition: avformat.h:798
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:253
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition: aviobuf.c:364
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition: aviobuf.c:633
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
Definition: avio_reading.c:42
#define AV_RB32
Definition: intreadwrite.h:130
#define AV_RB64
Definition: intreadwrite.h:164
#define flags(name, subs,...)
Definition: cbs_av1.c:561
#define s(width, name)
Definition: cbs_vp9.c:257
#define NULL
Definition: coverity.c:32
static int read_header(FFV1Context *f)
Definition: ffv1dec.c:545
@ AV_CODEC_ID_MP2
Definition: codec_id.h:424
@ AV_CODEC_ID_AC3
Definition: codec_id.h:427
@ AV_CODEC_ID_MPEG2VIDEO
preferred ID for MPEG-1/2 video decoding
Definition: codec_id.h:51
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition: avpacket.c:634
int av_new_packet(AVPacket *pkt, int size)
Allocate the payload of a packet and initialize its fields with default values.
Definition: avpacket.c:99
AVStream * avformat_new_stream(AVFormatContext *s, const AVCodec *c)
Add a new stream to a media file.
Definition: utils.c:4509
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
#define AVERROR_EOF
End of file.
Definition: error.h:55
#define AVERROR(e)
Definition: error.h:43
void * av_calloc(size_t nmemb, size_t size)
Non-inlined equivalent of av_mallocz_array().
Definition: mem.c:245
@ AVMEDIA_TYPE_AUDIO
Definition: avutil.h:202
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition: avutil.h:248
int i
Definition: input.c:407
void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition: utils.c:4945
static int read_probe(const AVProbeData *pd)
Definition: jvdec.c:55
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:117
static av_cold int read_close(AVFormatContext *ctx)
Definition: libcdio.c:145
#define AUDIO_ID
Definition: mpeg.h:41
#define VIDEO_ID
Definition: mpeg.h:42
static int64_t ff_parse_pes_pts(const uint8_t *buf)
Parse MPEG-PES five-byte timestamp.
Definition: mpeg.h:68
static const uint8_t header[24]
Definition: sdr2.c:67
static char buffer[20]
Definition: seek.c:32
enum AVMediaType codec_type
General type of the encoded data.
Definition: codec_par.h:56
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: codec_par.h:60
Format I/O context.
Definition: avformat.h:1232
Bytestream IO Context.
Definition: avio.h:161
const char * name
A comma separated list of short names for the format.
Definition: avformat.h:645
This structure stores compressed data.
Definition: packet.h:346
int stream_index
Definition: packet.h:371
int size
Definition: packet.h:370
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: packet.h:362
uint8_t * data
Definition: packet.h:369
This structure contains the data a format has to probe a file.
Definition: avformat.h:441
int buf_size
Size of buf except extra allocated bytes.
Definition: avformat.h:444
unsigned char * buf
Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero.
Definition: avformat.h:443
Stream structure.
Definition: avformat.h:873
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1038
enum AVStreamParseType need_parsing
Definition: avformat.h:1081
uint8_t chunk[CHUNK_SIZE]
Definition: ty.c:98
int64_t cur_pos
Definition: ty.c:78
TiVo_audio audio_type
Definition: ty.c:81
unsigned cur_chunk
Definition: ty.c:76
size_t ac3_pkt_size
Definition: ty.c:86
int cur_rec
Definition: ty.c:94
int64_t last_video_pts
Definition: ty.c:91
uint8_t pes_buffer[20]
Definition: ty.c:84
int pts_offset
Definition: ty.c:83
int pes_buf_cnt
Definition: ty.c:85
int64_t last_audio_pts
Definition: ty.c:90
int pes_length
Definition: ty.c:82
uint64_t last_ty_pts
Definition: ty.c:87
TiVo_series tivo_series
Definition: ty.c:80
TiVo_type tivo_type
Definition: ty.c:79
int num_recs
Definition: ty.c:95
TyRecHdr * rec_hdrs
Definition: ty.c:93
int64_t first_audio_pts
Definition: ty.c:89
unsigned cur_chunk_pos
Definition: ty.c:77
int first_chunk
Definition: ty.c:96
Definition: ty.c:49
uint64_t ty_pts
Definition: ty.c:54
int64_t rec_size
Definition: ty.c:50
uint8_t subrec_type
Definition: ty.c:53
uint8_t rec_type
Definition: ty.c:52
uint8_t ex[2]
Definition: ty.c:51
#define av_free(p)
#define ff_dlog(a,...)
#define av_freep(p)
AVPacket * pkt
Definition: movenc.c:59
int size
static int analyze_chunk(AVFormatContext *s, const uint8_t *chunk)
Definition: ty.c:167
static int check_sync_pes(AVFormatContext *s, AVPacket *pkt, int32_t offset, int32_t rec_len)
Definition: ty.c:473
static int ty_probe(const AVProbeData *p)
Definition: ty.c:101
#define DTIVO_PTS_OFFSET
Definition: ty.c:35
TiVo_series
Definition: ty.c:63
@ TIVO_SERIES_UNKNOWN
Definition: ty.c:64
@ TIVO_SERIES1
Definition: ty.c:65
@ TIVO_SERIES2
Definition: ty.c:66
static const uint8_t ty_MPEGAudioPacket[]
Definition: ty.c:42
static const uint8_t ty_AC3AudioPacket[]
Definition: ty.c:43
#define VIDEO_PTS_OFFSET
Definition: ty.c:38
#define TIVO_PES_FILEID
Definition: ty.c:45
static int ty_read_packet(AVFormatContext *s, AVPacket *pkt)
Definition: ty.c:653
static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
Definition: ty.c:511
#define VIDEO_PES_LENGTH
Definition: ty.c:34
AVInputFormat ff_ty_demuxer
Definition: ty.c:713
TiVo_audio
Definition: ty.c:69
@ TIVO_AUDIO_UNKNOWN
Definition: ty.c:70
@ TIVO_AUDIO_MPEG
Definition: ty.c:72
@ TIVO_AUDIO_AC3
Definition: ty.c:71
#define SA_PTS_OFFSET
Definition: ty.c:36
static int demux_video(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
Definition: ty.c:387
#define AC3_PTS_OFFSET
Definition: ty.c:37
#define SERIES1_PES_LENGTH
Definition: ty.c:31
static int get_chunk(AVFormatContext *s)
Definition: ty.c:334
static const uint8_t ty_VideoPacket[]
Definition: ty.c:41
TiVo_type
Definition: ty.c:57
@ TIVO_TYPE_DTIVO
Definition: ty.c:60
@ TIVO_TYPE_SA
Definition: ty.c:59
@ TIVO_TYPE_UNKNOWN
Definition: ty.c:58
static int ty_read_header(AVFormatContext *s)
Definition: ty.c:278
#define AC3_PES_LENGTH
Definition: ty.c:33
#define AC3_PKT_LENGTH
Definition: ty.c:39
#define SERIES2_PES_LENGTH
Definition: ty.c:32
static TyRecHdr * parse_chunk_headers(const uint8_t *buf, int num_recs)
Definition: ty.c:116
static int find_es_header(const uint8_t *header, const uint8_t *buffer, int search_len)
Definition: ty.c:155
#define CHUNK_SIZE
Definition: ty.c:46
static int ty_read_close(AVFormatContext *s)
Definition: ty.c:704
#define CHUNK_PEEK_COUNT
Definition: ty.c:47
static const uint8_t offset[127][2]
Definition: vf_spp.c:107
static double b1(void *priv, double x, double y)
Definition: vf_xfade.c:1665
static double b2(void *priv, double x, double y)
Definition: vf_xfade.c:1666