FFmpeg
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 "libavutil/mem.h"
28 #include "avformat.h"
29 #include "demux.h"
30 #include "internal.h"
31 #include "mpeg.h"
32 
33 #define SERIES1_PES_LENGTH 11 /* length of audio PES hdr on S1 */
34 #define SERIES2_PES_LENGTH 16 /* length of audio PES hdr on S2 */
35 #define AC3_PES_LENGTH 14 /* length of audio PES hdr for AC3 */
36 #define VIDEO_PES_LENGTH 16 /* length of video PES header */
37 #define DTIVO_PTS_OFFSET 6 /* offs into PES for MPEG PTS on DTivo */
38 #define SA_PTS_OFFSET 9 /* offset into PES for MPEG PTS on SA */
39 #define AC3_PTS_OFFSET 9 /* offset into PES for AC3 PTS on DTivo */
40 #define VIDEO_PTS_OFFSET 9 /* offset into PES for video PTS on all */
41 #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 #define TIVO_PES_FILEID 0xf5467abd
48 #define CHUNK_SIZE (128 * 1024)
49 #define CHUNK_PEEK_COUNT 3 /* number of chunks to probe */
50 
51 typedef struct TyRecHdr {
52  int64_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 {
63 } TiVo_type;
64 
65 typedef enum {
69 } TiVo_series;
70 
71 typedef enum {
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 */
99 
100  uint8_t chunk[CHUNK_SIZE];
102 
103 static int ty_probe(const AVProbeData *p)
104 {
105  int i;
106 
107  for (i = 0; i + 12 < p->buf_size; i += CHUNK_SIZE) {
108  if (AV_RB32(p->buf + i) == TIVO_PES_FILEID &&
109  AV_RB32(p->buf + i + 4) == 0x02 &&
110  AV_RB32(p->buf + i + 8) == CHUNK_SIZE) {
111  return AVPROBE_SCORE_MAX;
112  }
113  }
114 
115  return 0;
116 }
117 
118 static TyRecHdr *parse_chunk_headers(const uint8_t *buf,
119  int num_recs)
120 {
121  TyRecHdr *hdrs, *rec_hdr;
122  int i;
123 
124  hdrs = av_calloc(num_recs, sizeof(TyRecHdr));
125  if (!hdrs)
126  return NULL;
127 
128  for (i = 0; i < num_recs; i++) {
129  const uint8_t *record_header = buf + (i * 16);
130 
131  rec_hdr = &hdrs[i]; /* for brevity */
132  rec_hdr->rec_type = record_header[3];
133  rec_hdr->subrec_type = record_header[2] & 0x0f;
134  if ((record_header[0] & 0x80) == 0x80) {
135  uint8_t b1, b2;
136 
137  /* marker bit 2 set, so read extended data */
138  b1 = (((record_header[0] & 0x0f) << 4) |
139  ((record_header[1] & 0xf0) >> 4));
140  b2 = (((record_header[1] & 0x0f) << 4) |
141  ((record_header[2] & 0xf0) >> 4));
142 
143  rec_hdr->ex[0] = b1;
144  rec_hdr->ex[1] = b2;
145  rec_hdr->rec_size = 0;
146  rec_hdr->ty_pts = 0;
147  } else {
148  rec_hdr->rec_size = (record_header[0] << 8 |
149  record_header[1]) << 4 |
150  (record_header[2] >> 4);
151  rec_hdr->ty_pts = AV_RB64(&record_header[8]);
152  }
153  }
154  return hdrs;
155 }
156 
157 static int find_es_header(const uint8_t *header,
158  const uint8_t *buffer, int search_len)
159 {
160  int count;
161 
162  for (count = 0; count < search_len; count++) {
163  if (!memcmp(&buffer[count], header, 4))
164  return count;
165  }
166  return -1;
167 }
168 
169 static int analyze_chunk(AVFormatContext *s, const uint8_t *chunk)
170 {
171  TYDemuxContext *ty = s->priv_data;
172  int num_recs, i;
173  TyRecHdr *hdrs;
174  int num_6e0, num_be0, num_9c0, num_3c0;
175 
176  /* skip if it's a Part header */
177  if (AV_RB32(&chunk[0]) == TIVO_PES_FILEID)
178  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  num_recs = chunk[0];
183  if (num_recs < 5) {
184  /* try again with the next chunk. Sometimes there are dead ones */
185  return 0;
186  }
187 
188  chunk += 4; /* skip past rec count & SEQ bytes */
189  ff_dlog(s, "probe: chunk has %d recs\n", num_recs);
190  hdrs = parse_chunk_headers(chunk, num_recs);
191  if (!hdrs)
192  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  num_6e0 = num_be0 = num_9c0 = num_3c0 = 0;
202  for (i = 0; i < num_recs; i++) {
203  switch (hdrs[i].subrec_type << 8 | hdrs[i].rec_type) {
204  case 0x6e0:
205  num_6e0++;
206  break;
207  case 0xbe0:
208  num_be0++;
209  break;
210  case 0x3c0:
211  num_3c0++;
212  break;
213  case 0x9c0:
214  num_9c0++;
215  break;
216  }
217  }
218  ff_dlog(s, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.\n",
219  num_6e0, num_be0);
220 
221  /* set up our variables */
222  if (num_6e0 > 0) {
223  ff_dlog(s, "detected Series 1 Tivo\n");
226  } else if (num_be0 > 0) {
227  ff_dlog(s, "detected Series 2 Tivo\n");
230  }
231  if (num_9c0 > 0) {
232  ff_dlog(s, "detected AC-3 Audio (DTivo)\n");
237  } else if (num_3c0 > 0) {
239  ff_dlog(s, "detected MPEG Audio\n");
240  }
241 
242  /* if tivo_type still unknown, we can check PTS location
243  * in MPEG packets to determine tivo_type */
244  if (ty->tivo_type == TIVO_TYPE_UNKNOWN) {
245  uint32_t data_offset = 16 * num_recs;
246 
247  for (i = 0; i < num_recs; i++) {
248  if (data_offset + hdrs[i].rec_size > CHUNK_SIZE)
249  break;
250 
251  if ((hdrs[i].subrec_type << 8 | hdrs[i].rec_type) == 0x3c0 && hdrs[i].rec_size > 15) {
252  /* first make sure we're aligned */
253  int pes_offset = find_es_header(ty_MPEGAudioPacket,
254  &chunk[data_offset], 5);
255  if (pes_offset >= 0) {
256  /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
257  if ((chunk[data_offset + 6 + pes_offset] & 0x80) == 0x80) {
258  /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
259  if (ty->tivo_series == TIVO_SERIES1)
260  ff_dlog(s, "detected Stand-Alone Tivo\n");
261  ty->tivo_type = TIVO_TYPE_SA;
263  } else {
264  if (ty->tivo_series == TIVO_SERIES1)
265  ff_dlog(s, "detected DirecTV Tivo\n");
268  }
269  break;
270  }
271  }
272  data_offset += hdrs[i].rec_size;
273  }
274  }
275  av_free(hdrs);
276 
277  return 0;
278 }
279 
281 {
282  TYDemuxContext *ty = s->priv_data;
283  AVIOContext *pb = s->pb;
284  AVStream *st, *ast;
285  int i, ret = 0;
286 
290 
291  for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
292  avio_read(pb, ty->chunk, CHUNK_SIZE);
293 
294  ret = analyze_chunk(s, ty->chunk);
295  if (ret < 0)
296  return ret;
297  if (ty->tivo_series != TIVO_SERIES_UNKNOWN &&
300  break;
301  }
302 
303  if (ty->tivo_series == TIVO_SERIES_UNKNOWN ||
306  return AVERROR(EIO);
307 
308  st = avformat_new_stream(s, NULL);
309  if (!st)
310  return AVERROR(ENOMEM);
314  avpriv_set_pts_info(st, 64, 1, 90000);
315 
316  ast = avformat_new_stream(s, NULL);
317  if (!ast)
318  return AVERROR(ENOMEM);
320 
321  if (ty->audio_type == TIVO_AUDIO_MPEG) {
324  } else {
326  }
327  avpriv_set_pts_info(ast, 64, 1, 90000);
328 
329  ty->first_chunk = 1;
330 
331  avio_seek(pb, 0, SEEK_SET);
332 
333  return 0;
334 }
335 
337 {
338  TYDemuxContext *ty = s->priv_data;
339  AVIOContext *pb = s->pb;
340  int read_size, num_recs;
341 
342  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  if (avio_feof(pb))
346  return AVERROR_EOF;
347 
348  /* read the TY packet header */
349  read_size = avio_read(pb, ty->chunk, CHUNK_SIZE);
350  ty->cur_chunk++;
351 
352  if ((read_size < 4) || (AV_RB32(ty->chunk) == 0)) {
353  return AVERROR_EOF;
354  }
355 
356  /* check if it's a PART Header */
357  if (AV_RB32(ty->chunk) == TIVO_PES_FILEID) {
358  /* skip master chunk and read new chunk */
359  return get_chunk(s);
360  }
361 
362  /* number of records in chunk (8- or 16-bit number) */
363  if (ty->chunk[3] & 0x80) {
364  /* 16 bit rec cnt */
365  ty->num_recs = num_recs = (ty->chunk[1] << 8) + ty->chunk[0];
366  } else {
367  /* 8 bit reclen - TiVo 1.3 format */
368  ty->num_recs = num_recs = ty->chunk[0];
369  }
370  ty->cur_rec = 0;
371  ty->first_chunk = 0;
372 
373  ff_dlog(s, "chunk has %d records\n", num_recs);
374  ty->cur_chunk_pos = 4;
375 
376  av_freep(&ty->rec_hdrs);
377 
378  if (num_recs * 16 >= CHUNK_SIZE - 4)
379  return AVERROR_INVALIDDATA;
380 
381  ty->rec_hdrs = parse_chunk_headers(ty->chunk + 4, num_recs);
382  if (!ty->rec_hdrs)
383  return AVERROR(ENOMEM);
384  ty->cur_chunk_pos += 16 * num_recs;
385 
386  return 0;
387 }
388 
390 {
391  TYDemuxContext *ty = s->priv_data;
392  const int subrec_type = rec_hdr->subrec_type;
393  const int64_t rec_size = rec_hdr->rec_size;
394  int es_offset1, ret;
395  int got_packet = 0;
396 
397  if (subrec_type != 0x02 && subrec_type != 0x0c &&
398  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  es_offset1 = find_es_header(ty_VideoPacket, ty->chunk + ty->cur_chunk_pos, 5);
403  if (es_offset1 != -1) {
405  ty->chunk + ty->cur_chunk_pos + es_offset1 + VIDEO_PTS_OFFSET);
406  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  if (rec_size >= VIDEO_PES_LENGTH + es_offset1) {
411  int size = rec_hdr->rec_size - VIDEO_PES_LENGTH - es_offset1;
412 
413  ty->cur_chunk_pos += VIDEO_PES_LENGTH + es_offset1;
414  if ((ret = av_new_packet(pkt, size)) < 0)
415  return ret;
416  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, size);
417  ty->cur_chunk_pos += size;
418  pkt->stream_index = 0;
419  got_packet = 1;
420  } else {
421  ff_dlog(s, "video rec type 0x%02x has short PES"
422  " (%"PRId64" bytes)\n", subrec_type, rec_size);
423  /* nuke this block; it's too short, but has PES marker */
424  ty->cur_chunk_pos += rec_size;
425  return 0;
426  }
427  }
428  }
429  }
430 
431  if (subrec_type == 0x06) {
432  /* type 6 (S1 DTivo) has no data, so we're done */
433  ty->cur_chunk_pos += rec_size;
434  return 0;
435  }
436 
437  if (!got_packet) {
438  if ((ret = av_new_packet(pkt, rec_size)) < 0)
439  return ret;
440  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
441  ty->cur_chunk_pos += rec_size;
442  pkt->stream_index = 0;
443  got_packet = 1;
444  }
445 
446  /* if it's not a continue blk, then set PTS */
447  if (subrec_type != 0x02) {
448  if (subrec_type == 0x0c && pkt->size >= 6)
449  pkt->data[5] |= 0x08;
450  if (subrec_type == 0x07) {
451  ty->last_ty_pts = rec_hdr->ty_pts;
452  } 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  ty->last_ty_pts += 35000000;
460  //ty->last_ty_pts += 33366667;
461  }
462  /* set PTS for this block before we send */
463  if (ty->last_video_pts > AV_NOPTS_VALUE) {
464  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 */
469  }
470  }
471 
472  return got_packet;
473 }
474 
476  int32_t offset, int32_t rec_len)
477 {
478  TYDemuxContext *ty = s->priv_data;
479 
480  if (offset < 0 || offset + ty->pes_length > rec_len) {
481  /* entire PES header not present */
482  ff_dlog(s, "PES header at %"PRId32" not complete in record. storing.\n", offset);
483  /* save the partial pes header */
484  if (offset < 0) {
485  /* no header found, fake some 00's (this works, believe me) */
486  memset(ty->pes_buffer, 0, 4);
487  ty->pes_buf_cnt = 4;
488  if (rec_len > 4)
489  ff_dlog(s, "PES header not found in record of %"PRId32" bytes!\n", rec_len);
490  return -1;
491  }
492  /* copy the partial pes header we found */
493  memcpy(ty->pes_buffer, pkt->data + offset, rec_len - offset);
494  ty->pes_buf_cnt = rec_len - offset;
495 
496  if (offset > 0) {
497  /* PES Header was found, but not complete, so trim the end of this record */
498  pkt->size -= rec_len - offset;
499  return 1;
500  }
501  return -1; /* partial PES, no audio data */
502  }
503  /* full PES header present, extract PTS */
505  if (ty->first_audio_pts == AV_NOPTS_VALUE)
507  pkt->pts = ty->last_audio_pts;
508  memmove(pkt->data + offset, pkt->data + offset + ty->pes_length, rec_len - ty->pes_length);
509  pkt->size -= ty->pes_length;
510  return 0;
511 }
512 
514 {
515  TYDemuxContext *ty = s->priv_data;
516  const int subrec_type = rec_hdr->subrec_type;
517  const int64_t rec_size = rec_hdr->rec_size;
518  int es_offset1, ret;
519 
520  if (subrec_type == 2) {
521  int need = 0;
522  /* SA or DTiVo Audio Data, no PES (continued block)
523  * ================================================
524  */
525 
526  /* continue PES if previous was incomplete */
527  if (ty->pes_buf_cnt > 0) {
528  need = ty->pes_length - ty->pes_buf_cnt;
529 
530  ff_dlog(s, "continuing PES header\n");
531  /* do we have enough data to complete? */
532  if (need >= rec_size) {
533  /* don't have complete PES hdr; save what we have and return */
534  memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, rec_size);
535  ty->cur_chunk_pos += rec_size;
536  ty->pes_buf_cnt += rec_size;
537  return 0;
538  }
539 
540  /* we have enough; reconstruct this frame with the new hdr */
541  memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, need);
542  ty->cur_chunk_pos += need;
543  /* get the PTS out of this PES header (MPEG or AC3) */
544  if (ty->audio_type == TIVO_AUDIO_MPEG) {
545  es_offset1 = find_es_header(ty_MPEGAudioPacket,
546  ty->pes_buffer, 5);
547  } else {
548  es_offset1 = find_es_header(ty_AC3AudioPacket,
549  ty->pes_buffer, 5);
550  }
551  if (es_offset1 < 0) {
552  ff_dlog(s, "Can't find audio PES header in packet.\n");
553  } else {
555  &ty->pes_buffer[es_offset1 + ty->pts_offset]);
556  pkt->pts = ty->last_audio_pts;
557  }
558  ty->pes_buf_cnt = 0;
559 
560  }
561  if ((ret = av_new_packet(pkt, rec_size - need)) < 0)
562  return ret;
563  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size - need);
564  ty->cur_chunk_pos += rec_size - need;
565  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  if (ty->audio_type == TIVO_AUDIO_AC3 &&
574  ty->tivo_series == TIVO_SERIES2) {
575  if (ty->ac3_pkt_size + pkt->size > AC3_PKT_LENGTH) {
576  pkt->size -= 2;
577  ty->ac3_pkt_size = 0;
578  } else {
579  ty->ac3_pkt_size += pkt->size;
580  }
581  }
582  } else if (subrec_type == 0x03) {
583  if ((ret = av_new_packet(pkt, rec_size)) < 0)
584  return ret;
585  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
586  ty->cur_chunk_pos += rec_size;
587  pkt->stream_index = 1;
588  /* MPEG Audio with PES Header, either SA or DTiVo */
589  /* ================================================ */
590  es_offset1 = find_es_header(ty_MPEGAudioPacket, pkt->data, 5);
591 
592  /* SA PES Header, No Audio Data */
593  /* ================================================ */
594  if ((es_offset1 == 0) && (rec_size == 16)) {
596  if (ty->first_audio_pts == AV_NOPTS_VALUE)
599  return 0;
600  }
601  /* DTiVo Audio with PES Header */
602  /* ================================================ */
603 
604  /* Check for complete PES */
605  if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
606  /* partial PES header found, nothing else.
607  * we're done. */
609  return 0;
610  }
611  } else if (subrec_type == 0x04) {
612  /* SA Audio with no PES Header */
613  /* ================================================ */
614  if ((ret = av_new_packet(pkt, rec_size)) < 0)
615  return ret;
616  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
617  ty->cur_chunk_pos += rec_size;
618  pkt->stream_index = 1;
619  pkt->pts = ty->last_audio_pts;
620  } else if (subrec_type == 0x09) {
621  if ((ret = av_new_packet(pkt, rec_size)) < 0)
622  return ret;
623  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
624  ty->cur_chunk_pos += rec_size ;
625  pkt->stream_index = 1;
626 
627  /* DTiVo AC3 Audio Data with PES Header */
628  /* ================================================ */
629  es_offset1 = find_es_header(ty_AC3AudioPacket, pkt->data, 5);
630 
631  /* Check for complete PES */
632  if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
633  /* partial PES header found, nothing else. we're done. */
635  return 0;
636  }
637  /* S2 DTivo has invalid long AC3 packets */
638  if (ty->tivo_series == TIVO_SERIES2) {
639  if (pkt->size > AC3_PKT_LENGTH) {
640  pkt->size -= 2;
641  ty->ac3_pkt_size = 0;
642  } else {
643  ty->ac3_pkt_size = pkt->size;
644  }
645  }
646  } else {
647  /* Unsupported/Unknown */
648  ty->cur_chunk_pos += rec_size;
649  return 0;
650  }
651 
652  return 1;
653 }
654 
656 {
657  TYDemuxContext *ty = s->priv_data;
658  AVIOContext *pb = s->pb;
659  TyRecHdr *rec;
660  int64_t rec_size = 0;
661  int ret = 0;
662 
663  if (avio_feof(pb))
664  return AVERROR_EOF;
665 
666  while (ret <= 0) {
667  if (!ty->rec_hdrs || ty->first_chunk || ty->cur_rec >= ty->num_recs) {
668  if (get_chunk(s) < 0 || ty->num_recs <= 0)
669  return AVERROR_EOF;
670  }
671 
672  rec = &ty->rec_hdrs[ty->cur_rec];
673  rec_size = rec->rec_size;
674  ty->cur_rec++;
675 
676  if (rec_size <= 0)
677  continue;
678 
679  if (ty->cur_chunk_pos + rec->rec_size > CHUNK_SIZE)
680  return AVERROR_INVALIDDATA;
681 
682  if (avio_feof(pb))
683  return AVERROR_EOF;
684 
685  switch (rec->rec_type) {
686  case VIDEO_ID:
687  ret = demux_video(s, rec, pkt);
688  break;
689  case AUDIO_ID:
690  ret = demux_audio(s, rec, pkt);
691  break;
692  default:
693  ff_dlog(s, "Invalid record type 0x%02x\n", rec->rec_type);
694  case 0x01:
695  case 0x02:
696  case 0x03: /* TiVo data services */
697  case 0x05: /* unknown, but seen regularly */
698  ty->cur_chunk_pos += rec->rec_size;
699  break;
700  }
701  }
702 
703  return 0;
704 }
705 
707 {
708  TYDemuxContext *ty = s->priv_data;
709 
710  av_freep(&ty->rec_hdrs);
711 
712  return 0;
713 }
714 
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,
725 };
TYDemuxContext::ac3_pkt_size
size_t ac3_pkt_size
Definition: ty.c:88
av_packet_unref
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition: packet.c:427
ty_AC3AudioPacket
static const uint8_t ty_AC3AudioPacket[]
Definition: ty.c:45
AV_CODEC_ID_AC3
@ AV_CODEC_ID_AC3
Definition: codec_id.h:443
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
ty_read_header
static int ty_read_header(AVFormatContext *s)
Definition: ty.c:280
AVCodecParameters::codec_type
enum AVMediaType codec_type
General type of the encoded data.
Definition: codec_par.h:51
TiVo_audio
TiVo_audio
Definition: ty.c:71
ff_parse_pes_pts
static int64_t ff_parse_pes_pts(const uint8_t *buf)
Parse MPEG-PES five-byte timestamp.
Definition: mpeg.h:69
avformat_new_stream
AVStream * avformat_new_stream(AVFormatContext *s, const struct AVCodec *c)
Add a new stream to a media file.
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:57
find_es_header
static int find_es_header(const uint8_t *header, const uint8_t *buffer, int search_len)
Definition: ty.c:157
analyze_chunk
static int analyze_chunk(AVFormatContext *s, const uint8_t *chunk)
Definition: ty.c:169
ty_read_packet
static int ty_read_packet(AVFormatContext *s, AVPacket *pkt)
Definition: ty.c:655
AVPacket::data
uint8_t * data
Definition: packet.h:524
TYDemuxContext::last_audio_pts
int64_t last_audio_pts
Definition: ty.c:92
AUDIO_ID
#define AUDIO_ID
Definition: mpeg.h:41
AVProbeData::buf_size
int buf_size
Size of buf except extra allocated bytes.
Definition: avformat.h:454
AC3_PES_LENGTH
#define AC3_PES_LENGTH
Definition: ty.c:35
CHUNK_SIZE
#define CHUNK_SIZE
Definition: ty.c:48
TIVO_AUDIO_MPEG
@ TIVO_AUDIO_MPEG
Definition: ty.c:74
AVPROBE_SCORE_MAX
#define AVPROBE_SCORE_MAX
maximum score
Definition: avformat.h:463
TIVO_SERIES1
@ TIVO_SERIES1
Definition: ty.c:67
TiVo_type
TiVo_type
Definition: ty.c:59
avpriv_set_pts_info
void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition: avformat.c:853
b1
static double b1(void *priv, double x, double y)
Definition: vf_xfade.c:2035
TyRecHdr
Definition: ty.c:51
ffstream
static av_always_inline FFStream * ffstream(AVStream *st)
Definition: internal.h:417
read_close
static av_cold int read_close(AVFormatContext *ctx)
Definition: libcdio.c:143
TIVO_AUDIO_AC3
@ TIVO_AUDIO_AC3
Definition: ty.c:73
TYDemuxContext::cur_rec
int cur_rec
Definition: ty.c:96
TYDemuxContext::last_video_pts
int64_t last_video_pts
Definition: ty.c:93
ty_probe
static int ty_probe(const AVProbeData *p)
Definition: ty.c:103
SA_PTS_OFFSET
#define SA_PTS_OFFSET
Definition: ty.c:38
pkt
AVPacket * pkt
Definition: movenc.c:60
TYDemuxContext::first_chunk
int first_chunk
Definition: ty.c:98
read_packet
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
Definition: avio_read_callback.c:42
AC3_PKT_LENGTH
#define AC3_PKT_LENGTH
Definition: ty.c:41
intreadwrite.h
s
#define s(width, name)
Definition: cbs_vp9.c:198
CHUNK_PEEK_COUNT
#define CHUNK_PEEK_COUNT
Definition: ty.c:49
av_new_packet
int av_new_packet(AVPacket *pkt, int size)
Allocate the payload of a packet and initialize its fields with default values.
Definition: packet.c:98
demux_audio
static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
Definition: ty.c:513
AVInputFormat::name
const char * name
A comma separated list of short names for the format.
Definition: avformat.h:553
VIDEO_PTS_OFFSET
#define VIDEO_PTS_OFFSET
Definition: ty.c:40
AVProbeData::buf
unsigned char * buf
Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero.
Definition: avformat.h:453
AVMEDIA_TYPE_AUDIO
@ AVMEDIA_TYPE_AUDIO
Definition: avutil.h:202
AV_CODEC_ID_MP2
@ AV_CODEC_ID_MP2
Definition: codec_id.h:440
AC3_PTS_OFFSET
#define AC3_PTS_OFFSET
Definition: ty.c:39
TIVO_TYPE_SA
@ TIVO_TYPE_SA
Definition: ty.c:61
TyRecHdr::subrec_type
uint8_t subrec_type
Definition: ty.c:55
TYDemuxContext::pts_offset
int pts_offset
Definition: ty.c:85
TIVO_PES_FILEID
#define TIVO_PES_FILEID
Definition: ty.c:47
FFStream::need_parsing
enum AVStreamParseType need_parsing
Definition: internal.h:386
AVFormatContext
Format I/O context.
Definition: avformat.h:1255
need
must be printed separately If there s no standard function for printing the type you need
Definition: tablegen.txt:45
internal.h
AVStream::codecpar
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:766
parse_chunk_headers
static TyRecHdr * parse_chunk_headers(const uint8_t *buf, int num_recs)
Definition: ty.c:118
read_header
static int read_header(FFV1Context *f)
Definition: ffv1dec.c:550
NULL
#define NULL
Definition: coverity.c:32
DTIVO_PTS_OFFSET
#define DTIVO_PTS_OFFSET
Definition: ty.c:37
TYDemuxContext::cur_pos
int64_t cur_pos
Definition: ty.c:80
AVProbeData
This structure contains the data a format has to probe a file.
Definition: avformat.h:451
TyRecHdr::ex
uint8_t ex[2]
Definition: ty.c:53
TyRecHdr::rec_size
int64_t rec_size
Definition: ty.c:52
TYDemuxContext::last_ty_pts
uint64_t last_ty_pts
Definition: ty.c:89
ty_MPEGAudioPacket
static const uint8_t ty_MPEGAudioPacket[]
Definition: ty.c:44
TyRecHdr::ty_pts
uint64_t ty_pts
Definition: ty.c:56
TYDemuxContext::tivo_type
TiVo_type tivo_type
Definition: ty.c:81
ff_dlog
#define ff_dlog(a,...)
Definition: tableprint_vlc.h:28
AVIOContext
Bytestream IO Context.
Definition: avio.h:160
AVPacket::size
int size
Definition: packet.h:525
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:94
TYDemuxContext::rec_hdrs
TyRecHdr * rec_hdrs
Definition: ty.c:95
size
int size
Definition: twinvq_data.h:10344
AV_NOPTS_VALUE
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition: avutil.h:248
AV_RB32
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_WB64 unsigned int_TMPL AV_RB32
Definition: bytestream.h:96
FFInputFormat::p
AVInputFormat p
The public AVInputFormat.
Definition: demux.h:41
header
static const uint8_t header[24]
Definition: sdr2.c:68
b2
static double b2(void *priv, double x, double y)
Definition: vf_xfade.c:2036
mpeg.h
offset
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf offset
Definition: writing_filters.txt:86
get_chunk
static int get_chunk(AVFormatContext *s)
Definition: ty.c:336
TIVO_AUDIO_UNKNOWN
@ TIVO_AUDIO_UNKNOWN
Definition: ty.c:72
VIDEO_PES_LENGTH
#define VIDEO_PES_LENGTH
Definition: ty.c:36
TiVo_series
TiVo_series
Definition: ty.c:65
TYDemuxContext::pes_buffer
uint8_t pes_buffer[20]
Definition: ty.c:86
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
AVPacket::pts
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: packet.h:517
TYDemuxContext::pes_buf_cnt
int pes_buf_cnt
Definition: ty.c:87
TYDemuxContext::cur_chunk_pos
unsigned cur_chunk_pos
Definition: ty.c:79
TIVO_SERIES_UNKNOWN
@ TIVO_SERIES_UNKNOWN
Definition: ty.c:66
SERIES2_PES_LENGTH
#define SERIES2_PES_LENGTH
Definition: ty.c:34
TYDemuxContext::first_audio_pts
int64_t first_audio_pts
Definition: ty.c:91
demux.h
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:264
ret
ret
Definition: filter_design.txt:187
AVStream
Stream structure.
Definition: avformat.h:743
avio_seek
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:231
avformat.h
TIVO_TYPE_DTIVO
@ TIVO_TYPE_DTIVO
Definition: ty.c:62
TYDemuxContext::audio_type
TiVo_audio audio_type
Definition: ty.c:83
VIDEO_ID
#define VIDEO_ID
Definition: mpeg.h:42
TYDemuxContext::pes_length
int pes_length
Definition: ty.c:84
buffer
the frame and frame reference mechanism is intended to as much as expensive copies of that data while still allowing the filters to produce correct results The data is stored in buffers represented by AVFrame structures Several references can point to the same frame buffer
Definition: filter_design.txt:49
ff_ty_demuxer
const FFInputFormat ff_ty_demuxer
Definition: ty.c:715
avio_read
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition: aviobuf.c:612
AVSTREAM_PARSE_FULL_RAW
@ AVSTREAM_PARSE_FULL_RAW
full parsing and repack with timestamp and position generation by parser for raw this assumes that ea...
Definition: avformat.h:597
AVPacket::stream_index
int stream_index
Definition: packet.h:526
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
read_probe
static int read_probe(const AVProbeData *p)
Definition: cdg.c:30
AVFMT_TS_DISCONT
#define AVFMT_TS_DISCONT
Format allows timestamp discontinuities.
Definition: avformat.h:481
mem.h
demux_video
static int demux_video(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
Definition: ty.c:389
TYDemuxContext::num_recs
int num_recs
Definition: ty.c:97
av_free
#define av_free(p)
Definition: tableprint_vlc.h:33
AVCodecParameters::codec_id
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: codec_par.h:55
TYDemuxContext
Definition: ty.c:77
AVPacket
This structure stores compressed data.
Definition: packet.h:501
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
FFInputFormat
Definition: demux.h:37
int32_t
int32_t
Definition: audioconvert.c:56
TYDemuxContext::cur_chunk
unsigned cur_chunk
Definition: ty.c:78
SERIES1_PES_LENGTH
#define SERIES1_PES_LENGTH
Definition: ty.c:33
check_sync_pes
static int check_sync_pes(AVFormatContext *s, AVPacket *pkt, int32_t offset, int32_t rec_len)
Definition: ty.c:475
TYDemuxContext::tivo_series
TiVo_series tivo_series
Definition: ty.c:82
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61
ty_VideoPacket
static const uint8_t ty_VideoPacket[]
Definition: ty.c:43
TIVO_TYPE_UNKNOWN
@ TIVO_TYPE_UNKNOWN
Definition: ty.c:60
TYDemuxContext::chunk
uint8_t chunk[CHUNK_SIZE]
Definition: ty.c:100
ty_read_close
static int ty_read_close(AVFormatContext *s)
Definition: ty.c:706
TIVO_SERIES2
@ TIVO_SERIES2
Definition: ty.c:68
AV_CODEC_ID_MPEG2VIDEO
@ AV_CODEC_ID_MPEG2VIDEO
preferred ID for MPEG-1/2 video decoding
Definition: codec_id.h:54
AV_RB64
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_RB64
Definition: bytestream.h:95
TyRecHdr::rec_type
uint8_t rec_type
Definition: ty.c:54
avio_feof
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition: aviobuf.c:346