ffmpeg / libavformat / 4xm.c @ 72415b2a
History | View | Annotate | Download (11.1 KB)
1 | cef4ba9e | Mike Melanson | /*
|
---|---|---|---|
2 | * 4X Technologies .4xm File Demuxer (no muxer)
|
||
3 | * Copyright (c) 2003 The ffmpeg Project
|
||
4 | *
|
||
5 | b78e7197 | Diego Biurrun | * This file is part of FFmpeg.
|
6 | *
|
||
7 | * FFmpeg is free software; you can redistribute it and/or
|
||
8 | cef4ba9e | Mike Melanson | * modify it under the terms of the GNU Lesser General Public
|
9 | * License as published by the Free Software Foundation; either
|
||
10 | b78e7197 | Diego Biurrun | * version 2.1 of the License, or (at your option) any later version.
|
11 | cef4ba9e | Mike Melanson | *
|
12 | b78e7197 | Diego Biurrun | * FFmpeg is distributed in the hope that it will be useful,
|
13 | cef4ba9e | Mike Melanson | * 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.
|
||
16 | *
|
||
17 | * You should have received a copy of the GNU Lesser General Public
|
||
18 | b78e7197 | Diego Biurrun | * License along with FFmpeg; if not, write to the Free Software
|
19 | 5509bffa | Diego Biurrun | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
20 | cef4ba9e | Mike Melanson | */
|
21 | |||
22 | /**
|
||
23 | bad5537e | Diego Biurrun | * @file libavformat/4xm.c
|
24 | cef4ba9e | Mike Melanson | * 4X Technologies file demuxer
|
25 | * by Mike Melanson (melanson@pcisys.net)
|
||
26 | * for more information on the .4xm file format, visit:
|
||
27 | * http://www.pcisys.net/~melanson/codecs/
|
||
28 | */
|
||
29 | |||
30 | 6a5d31ac | Diego Biurrun | #include "libavutil/intreadwrite.h" |
31 | cef4ba9e | Mike Melanson | #include "avformat.h" |
32 | |||
33 | 470bce2b | Diego Biurrun | #define RIFF_TAG MKTAG('R', 'I', 'F', 'F') |
34 | c54286ab | Diego Biurrun | #define FOURXMV_TAG MKTAG('4', 'X', 'M', 'V') |
35 | 470bce2b | Diego Biurrun | #define LIST_TAG MKTAG('L', 'I', 'S', 'T') |
36 | #define HEAD_TAG MKTAG('H', 'E', 'A', 'D') |
||
37 | #define TRK__TAG MKTAG('T', 'R', 'K', '_') |
||
38 | #define MOVI_TAG MKTAG('M', 'O', 'V', 'I') |
||
39 | #define VTRK_TAG MKTAG('V', 'T', 'R', 'K') |
||
40 | #define STRK_TAG MKTAG('S', 'T', 'R', 'K') |
||
41 | #define std__TAG MKTAG('s', 't', 'd', '_') |
||
42 | #define name_TAG MKTAG('n', 'a', 'm', 'e') |
||
43 | #define vtrk_TAG MKTAG('v', 't', 'r', 'k') |
||
44 | #define strk_TAG MKTAG('s', 't', 'r', 'k') |
||
45 | #define ifrm_TAG MKTAG('i', 'f', 'r', 'm') |
||
46 | #define pfrm_TAG MKTAG('p', 'f', 'r', 'm') |
||
47 | #define cfrm_TAG MKTAG('c', 'f', 'r', 'm') |
||
48 | #define ifr2_TAG MKTAG('i', 'f', 'r', '2') |
||
49 | #define pfr2_TAG MKTAG('p', 'f', 'r', '2') |
||
50 | #define cfr2_TAG MKTAG('c', 'f', 'r', '2') |
||
51 | #define snd__TAG MKTAG('s', 'n', 'd', '_') |
||
52 | cef4ba9e | Mike Melanson | |
53 | #define vtrk_SIZE 0x44 |
||
54 | #define strk_SIZE 0x28 |
||
55 | |||
56 | #define GET_LIST_HEADER() \
|
||
57 | 8e7284ba | Mike Melanson | fourcc_tag = get_le32(pb); \ |
58 | cef4ba9e | Mike Melanson | size = get_le32(pb); \ |
59 | if (fourcc_tag != LIST_TAG) \
|
||
60 | return AVERROR_INVALIDDATA; \
|
||
61 | 8e7284ba | Mike Melanson | fourcc_tag = get_le32(pb); |
62 | cef4ba9e | Mike Melanson | |
63 | typedef struct AudioTrack { |
||
64 | int sample_rate;
|
||
65 | int bits;
|
||
66 | int channels;
|
||
67 | 8e7284ba | Mike Melanson | int stream_index;
|
68 | 4b465299 | Michael Niedermayer | int adpcm;
|
69 | 9913860b | Michael Niedermayer | int64_t audio_pts; |
70 | cef4ba9e | Mike Melanson | } AudioTrack; |
71 | |||
72 | typedef struct FourxmDemuxContext { |
||
73 | int width;
|
||
74 | int height;
|
||
75 | 8e7284ba | Mike Melanson | int video_stream_index;
|
76 | cef4ba9e | Mike Melanson | int track_count;
|
77 | AudioTrack *tracks; |
||
78 | 8e7284ba | Mike Melanson | |
79 | d66dae57 | Mike Melanson | int64_t video_pts; |
80 | 11498da3 | Mike Melanson | float fps;
|
81 | cef4ba9e | Mike Melanson | } FourxmDemuxContext; |
82 | |||
83 | static int fourxm_probe(AVProbeData *p) |
||
84 | { |
||
85 | fead30d4 | Alex Beregszaszi | if ((AV_RL32(&p->buf[0]) != RIFF_TAG) || |
86 | c54286ab | Diego Biurrun | (AV_RL32(&p->buf[8]) != FOURXMV_TAG))
|
87 | cef4ba9e | Mike Melanson | return 0; |
88 | |||
89 | return AVPROBE_SCORE_MAX;
|
||
90 | } |
||
91 | |||
92 | static int fourxm_read_header(AVFormatContext *s, |
||
93 | 8e7284ba | Mike Melanson | AVFormatParameters *ap) |
94 | cef4ba9e | Mike Melanson | { |
95 | 899681cd | Björn Axelsson | ByteIOContext *pb = s->pb; |
96 | cef4ba9e | Mike Melanson | unsigned int fourcc_tag; |
97 | unsigned int size; |
||
98 | int header_size;
|
||
99 | e4141433 | Nicholas Tung | FourxmDemuxContext *fourxm = s->priv_data; |
100 | cef4ba9e | Mike Melanson | unsigned char *header; |
101 | 68e1794e | Michael Niedermayer | int i, ret;
|
102 | cef4ba9e | Mike Melanson | AVStream *st; |
103 | |||
104 | fourxm->track_count = 0;
|
||
105 | fourxm->tracks = NULL;
|
||
106 | 11498da3 | Mike Melanson | fourxm->fps = 1.0; |
107 | cef4ba9e | Mike Melanson | |
108 | /* skip the first 3 32-bit numbers */
|
||
109 | url_fseek(pb, 12, SEEK_CUR);
|
||
110 | |||
111 | /* check for LIST-HEAD */
|
||
112 | GET_LIST_HEADER(); |
||
113 | header_size = size - 4;
|
||
114 | 59afda9f | Michael Niedermayer | if (fourcc_tag != HEAD_TAG || header_size < 0) |
115 | cef4ba9e | Mike Melanson | return AVERROR_INVALIDDATA;
|
116 | |||
117 | /* allocate space for the header and load the whole thing */
|
||
118 | header = av_malloc(header_size); |
||
119 | if (!header)
|
||
120 | 769e10f0 | Panagiotis Issaris | return AVERROR(ENOMEM);
|
121 | cc988dd7 | Michael Niedermayer | if (get_buffer(pb, header, header_size) != header_size){
|
122 | av_free(header); |
||
123 | 6f3e0b21 | Panagiotis Issaris | return AVERROR(EIO);
|
124 | cc988dd7 | Michael Niedermayer | } |
125 | cef4ba9e | Mike Melanson | |
126 | /* take the lazy approach and search for any and all vtrk and strk chunks */
|
||
127 | for (i = 0; i < header_size - 8; i++) { |
||
128 | fead30d4 | Alex Beregszaszi | fourcc_tag = AV_RL32(&header[i]); |
129 | size = AV_RL32(&header[i + 4]);
|
||
130 | cef4ba9e | Mike Melanson | |
131 | d66dae57 | Mike Melanson | if (fourcc_tag == std__TAG) {
|
132 | fead30d4 | Alex Beregszaszi | fourxm->fps = av_int2flt(AV_RL32(&header[i + 12]));
|
133 | d66dae57 | Mike Melanson | } else if (fourcc_tag == vtrk_TAG) { |
134 | cef4ba9e | Mike Melanson | /* check that there is enough data */
|
135 | if (size != vtrk_SIZE) {
|
||
136 | 68e1794e | Michael Niedermayer | ret= AVERROR_INVALIDDATA; |
137 | goto fail;
|
||
138 | cef4ba9e | Mike Melanson | } |
139 | 4f989885 | Michael Niedermayer | fourxm->width = AV_RL32(&header[i + 36]);
|
140 | fead30d4 | Alex Beregszaszi | fourxm->height = AV_RL32(&header[i + 40]);
|
141 | 8e7284ba | Mike Melanson | |
142 | /* allocate a new AVStream */
|
||
143 | st = av_new_stream(s, 0);
|
||
144 | cc988dd7 | Michael Niedermayer | if (!st){
|
145 | 68e1794e | Michael Niedermayer | ret= AVERROR(ENOMEM); |
146 | goto fail;
|
||
147 | cc988dd7 | Michael Niedermayer | } |
148 | caf5fb95 | Michael Niedermayer | av_set_pts_info(st, 60, 1, fourxm->fps); |
149 | 8e7284ba | Mike Melanson | |
150 | fourxm->video_stream_index = st->index; |
||
151 | |||
152 | 72415b2a | Stefano Sabatini | st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
153 | 01f4895c | Michael Niedermayer | st->codec->codec_id = CODEC_ID_4XM; |
154 | b6f508bb | Roberto Togni | st->codec->extradata_size = 4;
|
155 | st->codec->extradata = av_malloc(4);
|
||
156 | AV_WL32(st->codec->extradata, AV_RL32(&header[i + 16]));
|
||
157 | 4f989885 | Michael Niedermayer | st->codec->width = fourxm->width; |
158 | 01f4895c | Michael Niedermayer | st->codec->height = fourxm->height; |
159 | 8e7284ba | Mike Melanson | |
160 | a985a940 | Michael Niedermayer | i += 8 + size;
|
161 | cef4ba9e | Mike Melanson | } else if (fourcc_tag == strk_TAG) { |
162 | a10f1cbb | Michael Niedermayer | int current_track;
|
163 | cef4ba9e | Mike Melanson | /* check that there is enough data */
|
164 | if (size != strk_SIZE) {
|
||
165 | 68e1794e | Michael Niedermayer | ret= AVERROR_INVALIDDATA; |
166 | goto fail;
|
||
167 | cef4ba9e | Mike Melanson | } |
168 | fead30d4 | Alex Beregszaszi | current_track = AV_RL32(&header[i + 8]);
|
169 | 0838cfdc | Michael Niedermayer | if((unsigned)current_track >= UINT_MAX / sizeof(AudioTrack) - 1){ |
170 | av_log(s, AV_LOG_ERROR, "current_track too large\n");
|
||
171 | ret= -1;
|
||
172 | goto fail;
|
||
173 | } |
||
174 | cef4ba9e | Mike Melanson | if (current_track + 1 > fourxm->track_count) { |
175 | 5a634a69 | Mike Melanson | fourxm->track_count = current_track + 1;
|
176 | 115329f1 | Diego Biurrun | fourxm->tracks = av_realloc(fourxm->tracks, |
177 | 5a634a69 | Mike Melanson | fourxm->track_count * sizeof(AudioTrack));
|
178 | cef4ba9e | Mike Melanson | if (!fourxm->tracks) {
|
179 | 68e1794e | Michael Niedermayer | ret= AVERROR(ENOMEM); |
180 | goto fail;
|
||
181 | cef4ba9e | Mike Melanson | } |
182 | } |
||
183 | 4f989885 | Michael Niedermayer | fourxm->tracks[current_track].adpcm = AV_RL32(&header[i + 12]);
|
184 | fourxm->tracks[current_track].channels = AV_RL32(&header[i + 36]);
|
||
185 | fead30d4 | Alex Beregszaszi | fourxm->tracks[current_track].sample_rate = AV_RL32(&header[i + 40]);
|
186 | 4f989885 | Michael Niedermayer | fourxm->tracks[current_track].bits = AV_RL32(&header[i + 44]);
|
187 | 9913860b | Michael Niedermayer | fourxm->tracks[current_track].audio_pts = 0;
|
188 | cef4ba9e | Mike Melanson | i += 8 + size;
|
189 | 8e7284ba | Mike Melanson | |
190 | /* allocate a new AVStream */
|
||
191 | st = av_new_stream(s, current_track); |
||
192 | cc988dd7 | Michael Niedermayer | if (!st){
|
193 | 68e1794e | Michael Niedermayer | ret= AVERROR(ENOMEM); |
194 | goto fail;
|
||
195 | cc988dd7 | Michael Niedermayer | } |
196 | 8e7284ba | Mike Melanson | |
197 | caf5fb95 | Michael Niedermayer | av_set_pts_info(st, 60, 1, fourxm->tracks[current_track].sample_rate); |
198 | 9ee91c2f | Michael Niedermayer | |
199 | 8e7284ba | Mike Melanson | fourxm->tracks[current_track].stream_index = st->index; |
200 | |||
201 | 72415b2a | Stefano Sabatini | st->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
202 | 899af1a2 | Diego Biurrun | st->codec->codec_tag = 0;
|
203 | 4f989885 | Michael Niedermayer | st->codec->channels = fourxm->tracks[current_track].channels; |
204 | st->codec->sample_rate = fourxm->tracks[current_track].sample_rate; |
||
205 | dd1c8f3e | Luca Abeni | st->codec->bits_per_coded_sample = fourxm->tracks[current_track].bits; |
206 | 4f989885 | Michael Niedermayer | st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * |
207 | dd1c8f3e | Luca Abeni | st->codec->bits_per_coded_sample; |
208 | st->codec->block_align = st->codec->channels * st->codec->bits_per_coded_sample; |
||
209 | 0d29b7d5 | Michael Niedermayer | if (fourxm->tracks[current_track].adpcm){
|
210 | 01f4895c | Michael Niedermayer | st->codec->codec_id = CODEC_ID_ADPCM_4XM; |
211 | 0d29b7d5 | Michael Niedermayer | }else if (st->codec->bits_per_coded_sample == 8){ |
212 | 01f4895c | Michael Niedermayer | st->codec->codec_id = CODEC_ID_PCM_U8; |
213 | 0d29b7d5 | Michael Niedermayer | }else
|
214 | 01f4895c | Michael Niedermayer | st->codec->codec_id = CODEC_ID_PCM_S16LE; |
215 | cef4ba9e | Mike Melanson | } |
216 | } |
||
217 | |||
218 | /* skip over the LIST-MOVI chunk (which is where the stream should be */
|
||
219 | GET_LIST_HEADER(); |
||
220 | 68e1794e | Michael Niedermayer | if (fourcc_tag != MOVI_TAG){
|
221 | ret= AVERROR_INVALIDDATA; |
||
222 | goto fail;
|
||
223 | } |
||
224 | cef4ba9e | Mike Melanson | |
225 | 68e1794e | Michael Niedermayer | av_free(header); |
226 | 8e7284ba | Mike Melanson | /* initialize context members */
|
227 | caf5fb95 | Michael Niedermayer | fourxm->video_pts = -1; /* first frame will push to 0 */ |
228 | 8e7284ba | Mike Melanson | |
229 | cef4ba9e | Mike Melanson | return 0; |
230 | 68e1794e | Michael Niedermayer | fail:
|
231 | av_freep(&fourxm->tracks); |
||
232 | av_free(header); |
||
233 | return ret;
|
||
234 | cef4ba9e | Mike Melanson | } |
235 | |||
236 | static int fourxm_read_packet(AVFormatContext *s, |
||
237 | 8e7284ba | Mike Melanson | AVPacket *pkt) |
238 | cef4ba9e | Mike Melanson | { |
239 | FourxmDemuxContext *fourxm = s->priv_data; |
||
240 | 899681cd | Björn Axelsson | ByteIOContext *pb = s->pb; |
241 | cef4ba9e | Mike Melanson | unsigned int fourcc_tag; |
242 | 4b465299 | Michael Niedermayer | unsigned int size, out_size; |
243 | cef4ba9e | Mike Melanson | int ret = 0; |
244 | 9913860b | Michael Niedermayer | unsigned int track_number; |
245 | cef4ba9e | Mike Melanson | int packet_read = 0; |
246 | 8e7284ba | Mike Melanson | unsigned char header[8]; |
247 | d66dae57 | Mike Melanson | int audio_frame_count;
|
248 | cef4ba9e | Mike Melanson | |
249 | while (!packet_read) {
|
||
250 | |||
251 | 899681cd | Björn Axelsson | if ((ret = get_buffer(s->pb, header, 8)) < 0) |
252 | 8e7284ba | Mike Melanson | return ret;
|
253 | fead30d4 | Alex Beregszaszi | fourcc_tag = AV_RL32(&header[0]);
|
254 | size = AV_RL32(&header[4]);
|
||
255 | cef4ba9e | Mike Melanson | if (url_feof(pb))
|
256 | 6f3e0b21 | Panagiotis Issaris | return AVERROR(EIO);
|
257 | cef4ba9e | Mike Melanson | switch (fourcc_tag) {
|
258 | |||
259 | 73a19b2d | Mike Melanson | case LIST_TAG:
|
260 | d66dae57 | Mike Melanson | /* this is a good time to bump the video pts */
|
261 | caf5fb95 | Michael Niedermayer | fourxm->video_pts ++; |
262 | d66dae57 | Mike Melanson | |
263 | 73a19b2d | Mike Melanson | /* skip the LIST-* tag and move on to the next fourcc */
|
264 | get_le32(pb); |
||
265 | break;
|
||
266 | |||
267 | cef4ba9e | Mike Melanson | case ifrm_TAG:
|
268 | case pfrm_TAG:
|
||
269 | 07870f85 | Michael Niedermayer | case cfrm_TAG:
|
270 | case ifr2_TAG:
|
||
271 | case pfr2_TAG:
|
||
272 | case cfr2_TAG:
|
||
273 | 8e7284ba | Mike Melanson | /* allocate 8 more bytes than 'size' to account for fourcc
|
274 | * and size */
|
||
275 | 0ecca7a4 | Michael Niedermayer | if (size + 8 < size || av_new_packet(pkt, size + 8)) |
276 | 6f3e0b21 | Panagiotis Issaris | return AVERROR(EIO);
|
277 | 8e7284ba | Mike Melanson | pkt->stream_index = fourxm->video_stream_index; |
278 | d66dae57 | Mike Melanson | pkt->pts = fourxm->video_pts; |
279 | 899681cd | Björn Axelsson | pkt->pos = url_ftell(s->pb); |
280 | 8e7284ba | Mike Melanson | memcpy(pkt->data, header, 8);
|
281 | 899681cd | Björn Axelsson | ret = get_buffer(s->pb, &pkt->data[8], size);
|
282 | 8e7284ba | Mike Melanson | |
283 | 0d29b7d5 | Michael Niedermayer | if (ret < 0){ |
284 | 8e7284ba | Mike Melanson | av_free_packet(pkt); |
285 | 0d29b7d5 | Michael Niedermayer | }else
|
286 | 8e7284ba | Mike Melanson | packet_read = 1;
|
287 | 4a106616 | Michael Niedermayer | break;
|
288 | 8e7284ba | Mike Melanson | |
289 | cef4ba9e | Mike Melanson | case snd__TAG:
|
290 | track_number = get_le32(pb); |
||
291 | 4b465299 | Michael Niedermayer | out_size= get_le32(pb); |
292 | size-=8;
|
||
293 | |||
294 | 9913860b | Michael Niedermayer | if (track_number < fourxm->track_count) {
|
295 | 899681cd | Björn Axelsson | ret= av_get_packet(s->pb, pkt, size); |
296 | 2692067a | Michael Niedermayer | if(ret<0) |
297 | 6f3e0b21 | Panagiotis Issaris | return AVERROR(EIO);
|
298 | 115329f1 | Diego Biurrun | pkt->stream_index = |
299 | 9913860b | Michael Niedermayer | fourxm->tracks[track_number].stream_index; |
300 | pkt->pts = fourxm->tracks[track_number].audio_pts; |
||
301 | 2692067a | Michael Niedermayer | packet_read = 1;
|
302 | 8e7284ba | Mike Melanson | |
303 | d66dae57 | Mike Melanson | /* pts accounting */
|
304 | audio_frame_count = size; |
||
305 | 9913860b | Michael Niedermayer | if (fourxm->tracks[track_number].adpcm)
|
306 | 115329f1 | Diego Biurrun | audio_frame_count -= |
307 | 9913860b | Michael Niedermayer | 2 * (fourxm->tracks[track_number].channels);
|
308 | d66dae57 | Mike Melanson | audio_frame_count /= |
309 | 9913860b | Michael Niedermayer | fourxm->tracks[track_number].channels; |
310 | 0d29b7d5 | Michael Niedermayer | if (fourxm->tracks[track_number].adpcm){
|
311 | d66dae57 | Mike Melanson | audio_frame_count *= 2;
|
312 | 0d29b7d5 | Michael Niedermayer | }else
|
313 | d66dae57 | Mike Melanson | audio_frame_count /= |
314 | 9913860b | Michael Niedermayer | (fourxm->tracks[track_number].bits / 8);
|
315 | fourxm->tracks[track_number].audio_pts += audio_frame_count; |
||
316 | 8e7284ba | Mike Melanson | |
317 | cef4ba9e | Mike Melanson | } else {
|
318 | url_fseek(pb, size, SEEK_CUR); |
||
319 | } |
||
320 | break;
|
||
321 | |||
322 | default:
|
||
323 | url_fseek(pb, size, SEEK_CUR); |
||
324 | break;
|
||
325 | } |
||
326 | } |
||
327 | return ret;
|
||
328 | } |
||
329 | |||
330 | static int fourxm_read_close(AVFormatContext *s) |
||
331 | { |
||
332 | e4141433 | Nicholas Tung | FourxmDemuxContext *fourxm = s->priv_data; |
333 | cef4ba9e | Mike Melanson | |
334 | be195ed1 | Michael Niedermayer | av_freep(&fourxm->tracks); |
335 | cef4ba9e | Mike Melanson | |
336 | return 0; |
||
337 | } |
||
338 | |||
339 | ff70e601 | Måns Rullgård | AVInputFormat fourxm_demuxer = { |
340 | cef4ba9e | Mike Melanson | "4xm",
|
341 | bde15e74 | Stefano Sabatini | NULL_IF_CONFIG_SMALL("4X Technologies format"),
|
342 | cef4ba9e | Mike Melanson | sizeof(FourxmDemuxContext),
|
343 | fourxm_probe, |
||
344 | fourxm_read_header, |
||
345 | fourxm_read_packet, |
||
346 | fourxm_read_close, |
||
347 | }; |