]> git.sesse.net Git - ffmpeg/blob - libavformat/4xm.c
Fix build: Add intreadwrite.h and bswap.h #includes where necessary.
[ffmpeg] / libavformat / 4xm.c
1 /*
2  * 4X Technologies .4xm File Demuxer (no muxer)
3  * Copyright (c) 2003  The ffmpeg Project
4  *
5  * This file is part of FFmpeg.
6  *
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.
11  *
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.
16  *
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
20  */
21
22 /**
23  * @file 4xm.c
24  * 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 #include "libavutil/intreadwrite.h"
31 #include "avformat.h"
32
33 #define     RIFF_TAG MKTAG('R', 'I', 'F', 'F')
34 #define  FOURXMV_TAG MKTAG('4', 'X', 'M', 'V')
35 #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
53 #define vtrk_SIZE 0x44
54 #define strk_SIZE 0x28
55
56 #define GET_LIST_HEADER() \
57     fourcc_tag = get_le32(pb); \
58     size = get_le32(pb); \
59     if (fourcc_tag != LIST_TAG) \
60         return AVERROR_INVALIDDATA; \
61     fourcc_tag = get_le32(pb);
62
63 typedef struct AudioTrack {
64     int sample_rate;
65     int bits;
66     int channels;
67     int stream_index;
68     int adpcm;
69 } AudioTrack;
70
71 typedef struct FourxmDemuxContext {
72     int width;
73     int height;
74     int video_stream_index;
75     int track_count;
76     AudioTrack *tracks;
77     int selected_track;
78
79     int64_t audio_pts;
80     int64_t video_pts;
81     float fps;
82 } FourxmDemuxContext;
83
84 static int fourxm_probe(AVProbeData *p)
85 {
86     if ((AV_RL32(&p->buf[0]) != RIFF_TAG) ||
87         (AV_RL32(&p->buf[8]) != FOURXMV_TAG))
88         return 0;
89
90     return AVPROBE_SCORE_MAX;
91 }
92
93 static int fourxm_read_header(AVFormatContext *s,
94                               AVFormatParameters *ap)
95 {
96     ByteIOContext *pb = s->pb;
97     unsigned int fourcc_tag;
98     unsigned int size;
99     int header_size;
100     FourxmDemuxContext *fourxm = s->priv_data;
101     unsigned char *header;
102     int i;
103     int current_track = -1;
104     AVStream *st;
105
106     fourxm->track_count = 0;
107     fourxm->tracks = NULL;
108     fourxm->selected_track = 0;
109     fourxm->fps = 1.0;
110
111     /* skip the first 3 32-bit numbers */
112     url_fseek(pb, 12, SEEK_CUR);
113
114     /* check for LIST-HEAD */
115     GET_LIST_HEADER();
116     header_size = size - 4;
117     if (fourcc_tag != HEAD_TAG)
118         return AVERROR_INVALIDDATA;
119
120     /* allocate space for the header and load the whole thing */
121     header = av_malloc(header_size);
122     if (!header)
123         return AVERROR(ENOMEM);
124     if (get_buffer(pb, header, header_size) != header_size)
125         return AVERROR(EIO);
126
127     /* take the lazy approach and search for any and all vtrk and strk chunks */
128     for (i = 0; i < header_size - 8; i++) {
129         fourcc_tag = AV_RL32(&header[i]);
130         size = AV_RL32(&header[i + 4]);
131
132         if (fourcc_tag == std__TAG) {
133             fourxm->fps = av_int2flt(AV_RL32(&header[i + 12]));
134         } else if (fourcc_tag == vtrk_TAG) {
135             /* check that there is enough data */
136             if (size != vtrk_SIZE) {
137                 av_free(header);
138                 return AVERROR_INVALIDDATA;
139             }
140             fourxm->width = AV_RL32(&header[i + 36]);
141             fourxm->height = AV_RL32(&header[i + 40]);
142
143             /* allocate a new AVStream */
144             st = av_new_stream(s, 0);
145             if (!st)
146                 return AVERROR(ENOMEM);
147             av_set_pts_info(st, 60, 1, fourxm->fps);
148
149             fourxm->video_stream_index = st->index;
150
151             st->codec->codec_type = CODEC_TYPE_VIDEO;
152             st->codec->codec_id = CODEC_ID_4XM;
153             st->codec->extradata_size = 4;
154             st->codec->extradata = av_malloc(4);
155             AV_WL32(st->codec->extradata, AV_RL32(&header[i + 16]));
156             st->codec->width = fourxm->width;
157             st->codec->height = fourxm->height;
158
159             i += 8 + size;
160         } else if (fourcc_tag == strk_TAG) {
161             /* check that there is enough data */
162             if (size != strk_SIZE) {
163                 av_free(header);
164                 return AVERROR_INVALIDDATA;
165             }
166             current_track = AV_RL32(&header[i + 8]);
167             if (current_track + 1 > fourxm->track_count) {
168                 fourxm->track_count = current_track + 1;
169                 if((unsigned)fourxm->track_count >= UINT_MAX / sizeof(AudioTrack))
170                     return -1;
171                 fourxm->tracks = av_realloc(fourxm->tracks,
172                     fourxm->track_count * sizeof(AudioTrack));
173                 if (!fourxm->tracks) {
174                     av_free(header);
175                     return AVERROR(ENOMEM);
176                 }
177             }
178             fourxm->tracks[current_track].adpcm = AV_RL32(&header[i + 12]);
179             fourxm->tracks[current_track].channels = AV_RL32(&header[i + 36]);
180             fourxm->tracks[current_track].sample_rate = AV_RL32(&header[i + 40]);
181             fourxm->tracks[current_track].bits = AV_RL32(&header[i + 44]);
182             i += 8 + size;
183
184             /* allocate a new AVStream */
185             st = av_new_stream(s, current_track);
186             if (!st)
187                 return AVERROR(ENOMEM);
188
189             av_set_pts_info(st, 60, 1, fourxm->tracks[current_track].sample_rate);
190
191             fourxm->tracks[current_track].stream_index = st->index;
192
193             st->codec->codec_type = CODEC_TYPE_AUDIO;
194             st->codec->codec_tag = 0;
195             st->codec->channels = fourxm->tracks[current_track].channels;
196             st->codec->sample_rate = fourxm->tracks[current_track].sample_rate;
197             st->codec->bits_per_coded_sample = fourxm->tracks[current_track].bits;
198             st->codec->bit_rate = st->codec->channels * st->codec->sample_rate *
199                 st->codec->bits_per_coded_sample;
200             st->codec->block_align = st->codec->channels * st->codec->bits_per_coded_sample;
201             if (fourxm->tracks[current_track].adpcm)
202                 st->codec->codec_id = CODEC_ID_ADPCM_4XM;
203             else if (st->codec->bits_per_coded_sample == 8)
204                 st->codec->codec_id = CODEC_ID_PCM_U8;
205             else
206                 st->codec->codec_id = CODEC_ID_PCM_S16LE;
207         }
208     }
209
210     av_free(header);
211
212     /* skip over the LIST-MOVI chunk (which is where the stream should be */
213     GET_LIST_HEADER();
214     if (fourcc_tag != MOVI_TAG)
215         return AVERROR_INVALIDDATA;
216
217     /* initialize context members */
218     fourxm->video_pts = -1;  /* first frame will push to 0 */
219     fourxm->audio_pts = 0;
220
221     return 0;
222 }
223
224 static int fourxm_read_packet(AVFormatContext *s,
225                               AVPacket *pkt)
226 {
227     FourxmDemuxContext *fourxm = s->priv_data;
228     ByteIOContext *pb = s->pb;
229     unsigned int fourcc_tag;
230     unsigned int size, out_size;
231     int ret = 0;
232     int track_number;
233     int packet_read = 0;
234     unsigned char header[8];
235     int audio_frame_count;
236
237     while (!packet_read) {
238
239         if ((ret = get_buffer(s->pb, header, 8)) < 0)
240             return ret;
241         fourcc_tag = AV_RL32(&header[0]);
242         size = AV_RL32(&header[4]);
243         if (url_feof(pb))
244             return AVERROR(EIO);
245         switch (fourcc_tag) {
246
247         case LIST_TAG:
248             /* this is a good time to bump the video pts */
249             fourxm->video_pts ++;
250
251             /* skip the LIST-* tag and move on to the next fourcc */
252             get_le32(pb);
253             break;
254
255         case ifrm_TAG:
256         case pfrm_TAG:
257         case cfrm_TAG:
258         case ifr2_TAG:
259         case pfr2_TAG:
260         case cfr2_TAG:
261         {
262
263             /* allocate 8 more bytes than 'size' to account for fourcc
264              * and size */
265             if (size + 8 < size || av_new_packet(pkt, size + 8))
266                 return AVERROR(EIO);
267             pkt->stream_index = fourxm->video_stream_index;
268             pkt->pts = fourxm->video_pts;
269             pkt->pos = url_ftell(s->pb);
270             memcpy(pkt->data, header, 8);
271             ret = get_buffer(s->pb, &pkt->data[8], size);
272
273             if (ret < 0)
274                 av_free_packet(pkt);
275             else
276                 packet_read = 1;
277             break;
278         }
279
280         case snd__TAG:
281             track_number = get_le32(pb);
282             out_size= get_le32(pb);
283             size-=8;
284
285             if (track_number == fourxm->selected_track) {
286                 ret= av_get_packet(s->pb, pkt, size);
287                 if(ret<0)
288                     return AVERROR(EIO);
289                 pkt->stream_index =
290                     fourxm->tracks[fourxm->selected_track].stream_index;
291                 pkt->pts = fourxm->audio_pts;
292                 packet_read = 1;
293
294                 /* pts accounting */
295                 audio_frame_count = size;
296                 if (fourxm->tracks[fourxm->selected_track].adpcm)
297                     audio_frame_count -=
298                         2 * (fourxm->tracks[fourxm->selected_track].channels);
299                 audio_frame_count /=
300                       fourxm->tracks[fourxm->selected_track].channels;
301                 if (fourxm->tracks[fourxm->selected_track].adpcm)
302                     audio_frame_count *= 2;
303                 else
304                     audio_frame_count /=
305                     (fourxm->tracks[fourxm->selected_track].bits / 8);
306                 fourxm->audio_pts += audio_frame_count;
307
308             } else {
309                 url_fseek(pb, size, SEEK_CUR);
310             }
311             break;
312
313         default:
314             url_fseek(pb, size, SEEK_CUR);
315             break;
316         }
317     }
318     return ret;
319 }
320
321 static int fourxm_read_close(AVFormatContext *s)
322 {
323     FourxmDemuxContext *fourxm = s->priv_data;
324
325     av_free(fourxm->tracks);
326
327     return 0;
328 }
329
330 AVInputFormat fourxm_demuxer = {
331     "4xm",
332     NULL_IF_CONFIG_SMALL("4X Technologies format"),
333     sizeof(FourxmDemuxContext),
334     fourxm_probe,
335     fourxm_read_header,
336     fourxm_read_packet,
337     fourxm_read_close,
338 };