2 * TechnoTrend PVA (.pva) demuxer
3 * Copyright (c) 2007, 2008 Ivo van Poorten
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 #define PVA_MAX_PAYLOAD_LENGTH 0x17f8
27 #define PVA_VIDEO_PAYLOAD 0x01
28 #define PVA_AUDIO_PAYLOAD 0x02
29 #define PVA_MAGIC (('A' << 8) + 'V')
35 static int pva_check(const uint8_t *p
) {
36 int length
= AV_RB16(p
+ 6);
37 if (AV_RB16(p
) != PVA_MAGIC
|| !p
[2] || p
[2] > 2 || p
[4] != 0x55 ||
38 (p
[5] & 0xe0) || length
> PVA_MAX_PAYLOAD_LENGTH
)
43 static int pva_probe(AVProbeData
* pd
) {
44 const unsigned char *buf
= pd
->buf
;
45 int len
= pva_check(buf
);
50 if (pd
->buf_size
>= len
+ 8 &&
51 pva_check(buf
+ len
) >= 0)
52 return AVPROBE_SCORE_EXTENSION
;
54 return AVPROBE_SCORE_MAX
/ 4;
57 static int pva_read_header(AVFormatContext
*s
) {
60 if (!(st
= avformat_new_stream(s
, NULL
)))
61 return AVERROR(ENOMEM
);
62 st
->codec
->codec_type
= AVMEDIA_TYPE_VIDEO
;
63 st
->codec
->codec_id
= AV_CODEC_ID_MPEG2VIDEO
;
64 st
->need_parsing
= AVSTREAM_PARSE_FULL
;
65 avpriv_set_pts_info(st
, 32, 1, 90000);
66 av_add_index_entry(st
, 0, 0, 0, 0, AVINDEX_KEYFRAME
);
68 if (!(st
= avformat_new_stream(s
, NULL
)))
69 return AVERROR(ENOMEM
);
70 st
->codec
->codec_type
= AVMEDIA_TYPE_AUDIO
;
71 st
->codec
->codec_id
= AV_CODEC_ID_MP2
;
72 st
->need_parsing
= AVSTREAM_PARSE_FULL
;
73 avpriv_set_pts_info(st
, 33, 1, 90000);
74 av_add_index_entry(st
, 0, 0, 0, 0, AVINDEX_KEYFRAME
);
76 /* the parameters will be extracted from the compressed bitstream */
80 #define pva_log if (read_packet) av_log
82 static int read_part_of_packet(AVFormatContext
*s
, int64_t *pts
,
83 int *len
, int *strid
, int read_packet
) {
84 AVIOContext
*pb
= s
->pb
;
85 PVAContext
*pvactx
= s
->priv_data
;
86 int syncword
, streamid
, reserved
, flags
, length
, pts_flag
;
87 int64_t pva_pts
= AV_NOPTS_VALUE
, startpos
;
91 startpos
= avio_tell(pb
);
93 syncword
= avio_rb16(pb
);
94 streamid
= avio_r8(pb
);
95 avio_r8(pb
); /* counter not used */
96 reserved
= avio_r8(pb
);
98 length
= avio_rb16(pb
);
100 pts_flag
= flags
& 0x10;
102 if (syncword
!= PVA_MAGIC
) {
103 pva_log(s
, AV_LOG_ERROR
, "invalid syncword\n");
106 if (streamid
!= PVA_VIDEO_PAYLOAD
&& streamid
!= PVA_AUDIO_PAYLOAD
) {
107 pva_log(s
, AV_LOG_ERROR
, "invalid streamid\n");
110 if (reserved
!= 0x55) {
111 pva_log(s
, AV_LOG_WARNING
, "expected reserved byte to be 0x55\n");
113 if (length
> PVA_MAX_PAYLOAD_LENGTH
) {
114 pva_log(s
, AV_LOG_ERROR
, "invalid payload length %u\n", length
);
118 if (streamid
== PVA_VIDEO_PAYLOAD
&& pts_flag
) {
119 pva_pts
= avio_rb32(pb
);
121 } else if (streamid
== PVA_AUDIO_PAYLOAD
) {
122 /* PVA Audio Packets either start with a signaled PES packet or
123 * are a continuation of the previous PES packet. New PES packets
124 * always start at the beginning of a PVA Packet, never somewhere in
126 if (!pvactx
->continue_pes
) {
127 int pes_signal
, pes_header_data_length
, pes_packet_length
,
129 unsigned char pes_header_data
[256];
131 pes_signal
= avio_rb24(pb
);
133 pes_packet_length
= avio_rb16(pb
);
134 pes_flags
= avio_rb16(pb
);
135 pes_header_data_length
= avio_r8(pb
);
137 if (pes_signal
!= 1 || pes_header_data_length
== 0) {
138 pva_log(s
, AV_LOG_WARNING
, "expected non empty signaled PES packet, "
139 "trying to recover\n");
140 avio_skip(pb
, length
- 9);
146 ret
= avio_read(pb
, pes_header_data
, pes_header_data_length
);
147 if (ret
!= pes_header_data_length
)
148 return ret
< 0 ? ret
: AVERROR_INVALIDDATA
;
149 length
-= 9 + pes_header_data_length
;
151 pes_packet_length
-= 3 + pes_header_data_length
;
153 pvactx
->continue_pes
= pes_packet_length
;
155 if (pes_flags
& 0x80 && (pes_header_data
[0] & 0xf0) == 0x20) {
156 if (pes_header_data_length
< 5) {
157 pva_log(s
, AV_LOG_ERROR
, "header too short\n");
158 avio_skip(pb
, length
);
159 return AVERROR_INVALIDDATA
;
161 pva_pts
= ff_parse_pes_pts(pes_header_data
);
165 pvactx
->continue_pes
-= length
;
167 if (pvactx
->continue_pes
< 0) {
168 pva_log(s
, AV_LOG_WARNING
, "audio data corruption\n");
169 pvactx
->continue_pes
= 0;
173 if (pva_pts
!= AV_NOPTS_VALUE
)
174 av_add_index_entry(s
->streams
[streamid
-1], startpos
, pva_pts
, 0, 0, AVINDEX_KEYFRAME
);
182 static int pva_read_packet(AVFormatContext
*s
, AVPacket
*pkt
) {
183 AVIOContext
*pb
= s
->pb
;
185 int ret
, length
, streamid
;
187 if (read_part_of_packet(s
, &pva_pts
, &length
, &streamid
, 1) < 0 ||
188 (ret
= av_get_packet(pb
, pkt
, length
)) <= 0)
191 pkt
->stream_index
= streamid
- 1;
197 static int64_t pva_read_timestamp(struct AVFormatContext
*s
, int stream_index
,
198 int64_t *pos
, int64_t pos_limit
) {
199 AVIOContext
*pb
= s
->pb
;
200 PVAContext
*pvactx
= s
->priv_data
;
201 int length
, streamid
;
202 int64_t res
= AV_NOPTS_VALUE
;
204 pos_limit
= FFMIN(*pos
+PVA_MAX_PAYLOAD_LENGTH
*8, (uint64_t)*pos
+pos_limit
);
206 while (*pos
< pos_limit
) {
207 res
= AV_NOPTS_VALUE
;
208 avio_seek(pb
, *pos
, SEEK_SET
);
210 pvactx
->continue_pes
= 0;
211 if (read_part_of_packet(s
, &res
, &length
, &streamid
, 0)) {
215 if (streamid
- 1 != stream_index
|| res
== AV_NOPTS_VALUE
) {
216 *pos
= avio_tell(pb
) + length
;
222 pvactx
->continue_pes
= 0;
226 AVInputFormat ff_pva_demuxer
= {
228 .long_name
= NULL_IF_CONFIG_SMALL("TechnoTrend PVA"),
229 .priv_data_size
= sizeof(PVAContext
),
230 .read_probe
= pva_probe
,
231 .read_header
= pva_read_header
,
232 .read_packet
= pva_read_packet
,
233 .read_timestamp
= pva_read_timestamp
,