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
25 #define PVA_MAX_PAYLOAD_LENGTH 0x17f8
26 #define PVA_VIDEO_PAYLOAD 0x01
27 #define PVA_AUDIO_PAYLOAD 0x02
28 #define PVA_MAGIC (('A' << 8) + 'V')
34 static int pva_probe(AVProbeData
* pd
) {
35 unsigned char *buf
= pd
->buf
;
37 if (AV_RB16(buf
) == PVA_MAGIC
&& buf
[2] && buf
[2] < 3 && buf
[4] == 0x55)
38 return AVPROBE_SCORE_MAX
/ 2;
43 static int pva_read_header(AVFormatContext
*s
, AVFormatParameters
*ap
) {
46 if (!(st
= av_new_stream(s
, 0)))
47 return AVERROR(ENOMEM
);
48 st
->codec
->codec_type
= CODEC_TYPE_VIDEO
;
49 st
->codec
->codec_id
= CODEC_ID_MPEG2VIDEO
;
50 st
->need_parsing
= AVSTREAM_PARSE_FULL
;
51 av_set_pts_info(st
, 32, 1, 90000);
52 av_add_index_entry(st
, 0, 0, 0, 0, AVINDEX_KEYFRAME
);
54 if (!(st
= av_new_stream(s
, 1)))
55 return AVERROR(ENOMEM
);
56 st
->codec
->codec_type
= CODEC_TYPE_AUDIO
;
57 st
->codec
->codec_id
= CODEC_ID_MP2
;
58 st
->need_parsing
= AVSTREAM_PARSE_FULL
;
59 av_set_pts_info(st
, 33, 1, 90000);
60 av_add_index_entry(st
, 0, 0, 0, 0, AVINDEX_KEYFRAME
);
62 /* the parameters will be extracted from the compressed bitstream */
66 #define pva_log if (read_packet) av_log
68 static int read_part_of_packet(AVFormatContext
*s
, int64_t *pts
,
69 int *len
, int *strid
, int read_packet
) {
70 ByteIOContext
*pb
= s
->pb
;
71 PVAContext
*pvactx
= s
->priv_data
;
72 int syncword
, streamid
, reserved
, flags
, length
, pts_flag
;
73 int64_t pva_pts
= AV_NOPTS_VALUE
, startpos
;
76 startpos
= url_ftell(pb
);
78 syncword
= get_be16(pb
);
79 streamid
= get_byte(pb
);
80 get_byte(pb
); /* counter not used */
81 reserved
= get_byte(pb
);
83 length
= get_be16(pb
);
85 pts_flag
= flags
& 0x10;
87 if (syncword
!= PVA_MAGIC
) {
88 pva_log(s
, AV_LOG_ERROR
, "invalid syncword\n");
91 if (streamid
!= PVA_VIDEO_PAYLOAD
&& streamid
!= PVA_AUDIO_PAYLOAD
) {
92 pva_log(s
, AV_LOG_ERROR
, "invalid streamid\n");
95 if (reserved
!= 0x55) {
96 pva_log(s
, AV_LOG_WARNING
, "expected reserved byte to be 0x55\n");
98 if (length
> PVA_MAX_PAYLOAD_LENGTH
) {
99 pva_log(s
, AV_LOG_ERROR
, "invalid payload length %u\n", length
);
103 if (streamid
== PVA_VIDEO_PAYLOAD
&& pts_flag
) {
104 pva_pts
= get_be32(pb
);
106 } else if (streamid
== PVA_AUDIO_PAYLOAD
) {
107 /* PVA Audio Packets either start with a signaled PES packet or
108 * are a continuation of the previous PES packet. New PES packets
109 * always start at the beginning of a PVA Packet, never somewhere in
111 if (!pvactx
->continue_pes
) {
112 int pes_signal
, pes_header_data_length
, pes_packet_length
,
114 unsigned char pes_header_data
[256];
116 pes_signal
= get_be24(pb
);
118 pes_packet_length
= get_be16(pb
);
119 pes_flags
= get_be16(pb
);
120 pes_header_data_length
= get_byte(pb
);
122 if (pes_signal
!= 1) {
123 pva_log(s
, AV_LOG_WARNING
, "expected signaled PES packet, "
124 "trying to recover\n");
125 url_fskip(pb
, length
- 9);
131 get_buffer(pb
, pes_header_data
, pes_header_data_length
);
132 length
-= 9 + pes_header_data_length
;
134 pes_packet_length
-= 3 + pes_header_data_length
;
136 pvactx
->continue_pes
= pes_packet_length
;
138 if (pes_flags
& 0x80 && (pes_header_data
[0] & 0xf0) == 0x20)
139 pva_pts
= ff_parse_pes_pts(pes_header_data
);
142 pvactx
->continue_pes
-= length
;
144 if (pvactx
->continue_pes
< 0) {
145 pva_log(s
, AV_LOG_WARNING
, "audio data corruption\n");
146 pvactx
->continue_pes
= 0;
150 if (pva_pts
!= AV_NOPTS_VALUE
)
151 av_add_index_entry(s
->streams
[streamid
-1], startpos
, pva_pts
, 0, 0, AVINDEX_KEYFRAME
);
159 static int pva_read_packet(AVFormatContext
*s
, AVPacket
*pkt
) {
160 ByteIOContext
*pb
= s
->pb
;
162 int ret
, length
, streamid
;
164 if (read_part_of_packet(s
, &pva_pts
, &length
, &streamid
, 1) < 0 ||
165 (ret
= av_get_packet(pb
, pkt
, length
)) <= 0)
168 pkt
->stream_index
= streamid
- 1;
174 static int64_t pva_read_timestamp(struct AVFormatContext
*s
, int stream_index
,
175 int64_t *pos
, int64_t pos_limit
) {
176 ByteIOContext
*pb
= s
->pb
;
177 PVAContext
*pvactx
= s
->priv_data
;
178 int length
, streamid
;
179 int64_t res
= AV_NOPTS_VALUE
;
181 pos_limit
= FFMIN(*pos
+PVA_MAX_PAYLOAD_LENGTH
*8, (uint64_t)*pos
+pos_limit
);
183 while (*pos
< pos_limit
) {
184 res
= AV_NOPTS_VALUE
;
185 url_fseek(pb
, *pos
, SEEK_SET
);
187 pvactx
->continue_pes
= 0;
188 if (read_part_of_packet(s
, &res
, &length
, &streamid
, 0)) {
192 if (streamid
- 1 != stream_index
|| res
== AV_NOPTS_VALUE
) {
193 *pos
= url_ftell(pb
) + length
;
199 pvactx
->continue_pes
= 0;
203 AVInputFormat pva_demuxer
= {
205 NULL_IF_CONFIG_SMALL("TechnoTrend PVA file and stream format"),
210 .read_timestamp
= pva_read_timestamp