Implemented audio access routines
[2oom.git] / src / module / ffmpeg.c
blobb65575fffc191933e14a8ae3cca0ce974d379989
1 /* 2ooM: The Master of Orion II Reverse Engineering Project
2 * Copyright (C) 2006 Nick Bowler
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * $HeadURL$
19 * $Date$
20 * $Revision$
21 * $Author$
24 #define FFMPEG_MODULE "AV-FFmpeg"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <stdint.h>
32 #include <ffmpeg/avformat.h>
33 #include <ffmpeg/avcodec.h>
35 #include "../plugin/plugin.h"
36 #include "../audio/avfile.h"
37 #include "../vfs/vfs.h"
38 #include "../sys/log.h"
40 #define init ffmpeg_LTX_init
42 #define MIN(a,b) (((a)<(b))?(a):(b))
44 static struct resource {
45 AVFormatContext *fmt;
46 AVCodecContext *actx, *vctx;
47 AVCodec *acodec, *vcodec;
48 int astream, vstream;
50 int16_t *aframe;
51 int aframelen, aframeoff;
53 struct audio_format afmt;
54 } *data;
55 static int datasz = 4;
57 static struct vfs_access vfs;
58 static struct log_context logger;
60 /* 2ooM VFS open handler for libavformat */
61 static int ff_vfs_open(URLContext *h, const char *filename, int flags)
63 struct vfs_file *f;
65 strstart(filename, "vfs:", &filename);
67 /* 2ooM VFS is read-only */
68 if (flags & URL_RDWR || flags & URL_WRONLY)
69 return -EPERM;
71 if (!(f = vfs.open(filename)))
72 return -ENOENT;
74 h->priv_data = f;
76 return 0;
79 static int ff_vfs_read(URLContext *h, unsigned char *buf, int size)
81 return vfs.read(h->priv_data, buf, size);
84 static int ff_vfs_write(URLContext *h, unsigned char *buf, int size)
86 return -1; /* 2ooM VFS is read-only */
89 static offset_t ff_vfs_seek(URLContext *h, offset_t pos, int whence)
91 return vfs.seek(h->priv_data, pos, whence);
94 static int ff_vfs_close(URLContext *h)
96 vfs.close(h->priv_data);
97 return 0;
100 static URLProtocol vfs_protocol = {
101 "vfs",
102 ff_vfs_open,
103 ff_vfs_read,
104 ff_vfs_write,
105 ff_vfs_seek,
106 ff_vfs_close,
107 NULL
110 /* Double the size of the buffer for storing resources */
111 static int moreresources(void) {
112 static int init;
113 struct resource *new;
114 int i;
116 if (!(new = realloc(data, sizeof *data * datasz*2)))
117 return -1;
119 data = new;
121 for (i = (!init) ? init = 1, 0 : datasz; i < datasz*2; i++)
122 data[i].fmt = NULL;
124 datasz *= 2;
126 return 0;
129 /* Locate the first stream matching type, initialize codecs, and return the
130 * stream ID.
132 static int getstream(enum CodecType type, AVCodec **codec, AVFormatContext *fmt)
134 AVCodecContext *ctx;
135 int i;
137 for (i = 0; i < fmt->nb_streams; i++)
138 if (fmt->streams[i]->codec->codec_type == type)
139 break;
141 if (i >= fmt->nb_streams) {
142 logger.write(LOG_DEBUG, FFMPEG_MODULE,
143 "Failed to find%s stream in %s",
144 (type == CODEC_TYPE_AUDIO) ? " audio" :
145 (type == CODEC_TYPE_VIDEO) ? " video" : "",
146 fmt->filename);
147 return -1;
150 ctx = fmt->streams[i]->codec;
152 if (!(*codec = avcodec_find_decoder(ctx->codec_id))) {
153 logger.write(LOG_DEBUG, FFMPEG_MODULE,
154 "Failed to find decoder for %s", ctx->codec_name);
155 return -1;
158 if (avcodec_open(ctx, *codec) < 0) {
159 logger.write(LOG_DEBUG, FFMPEG_MODULE,
160 "Failed to open decoder %s", (*codec)->name);
161 return -1;
164 return i;
167 /* Attempt to open a VFS resource with FFmpeg */
168 static int ff_av_open(const char *resource, int type,
169 const struct audio_format *afmt,
170 const struct video_format *vfmt)
172 char *url;
173 struct resource r = { .astream = -1, .vstream = -1 };
174 int i;
176 if (!(type & AV_INPUT_AUDIO || type & AV_INPUT_VIDEO))
177 return -1;
179 if (!(url = malloc(strlen("vfs:") + strlen(resource) + 1)))
180 return -1;
182 /* Find a free resource, or make one */
183 for (i = 0; i < datasz; i++)
184 if (data[i].fmt == NULL)
185 break;
186 if (i >= datasz)
187 if (moreresources())
188 goto err;
190 strcpy(url, "vfs:");
191 strcat(url, resource);
193 if (av_open_input_file(&r.fmt, url, NULL, 0, NULL) != 0) {
194 logger.write(LOG_DEBUG, FFMPEG_MODULE,
195 "Failed to open %s", url);
196 goto err;
199 if (av_find_stream_info(r.fmt) < 0) {
200 logger.write(LOG_DEBUG, FFMPEG_MODULE,
201 "Failed to find stream info for %s", url);
202 goto err;
205 if (type & AV_INPUT_AUDIO) {
206 r.astream = getstream(CODEC_TYPE_AUDIO, &r.acodec, r.fmt);
207 if (r.astream == -1)
208 goto err;
209 r.actx = r.fmt->streams[r.astream]->codec;
210 r.afmt = *afmt;
211 if (!(r.aframe = malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE)))
212 goto err;
214 if (type & AV_INPUT_VIDEO) {
215 r.vstream = getstream(CODEC_TYPE_VIDEO, &r.vcodec, r.fmt);
216 if (r.vstream == -1)
217 goto err;
218 r.vctx = r.fmt->streams[r.vstream]->codec;
221 data[i] = r;
222 return i;
223 err:
224 if (r.actx)
225 avcodec_close(r.actx);
226 if (r.vctx)
227 avcodec_close(r.vctx);
228 if (r.fmt)
229 av_close_input_file(r.fmt);
231 free(r.aframe);
233 free(url);
234 return -1;
237 /* Close an open resource */
238 static void ff_av_close(int id)
240 if (id >= datasz || !data[id].fmt)
241 return;
243 if (data[id].actx)
244 avcodec_close(data[id].actx);
245 if (data[id].vctx)
246 avcodec_close(data[id].vctx);
248 av_close_input_file(data[id].fmt);
249 data[id].fmt = NULL;
251 free(data[id].aframe);
253 return;
256 /* Read decoded audio data */
257 static int ff_av_read_audio(int id, void *buf, int len)
259 struct resource *d;
260 int written = 0;
261 char *stream = buf;
263 if (id >= datasz || !data[id].fmt || data[id].astream == -1)
264 return -1;
266 d = &data[id];
268 if (d->aframeoff < d->aframelen) {
269 if (d->aframelen - d->aframeoff >= len) {
270 memcpy(stream, d->aframe+(d->aframeoff/2), len);
271 d->aframeoff += len;
272 written += len;
273 return written;
274 } else {
275 memcpy(stream, d->aframe+(d->aframeoff/2),
276 d->aframelen - d->aframeoff);
278 written += d->aframelen - d->aframeoff;
279 len -= d->aframelen - d->aframeoff;
280 stream += d->aframelen - d->aframeoff;
284 while (len > 0) {
285 AVPacket packet;
286 d->aframelen = 0, d->aframeoff = 0;
287 if (av_read_frame(d->fmt, &packet) < 0)
288 break;
290 if (packet.stream_index == d->astream) {
291 avcodec_decode_audio(d->actx, d->aframe, &d->aframelen,
292 packet.data, packet.size);
294 if (d->aframelen <= 0)
295 return av_free_packet(&packet), written;
297 memcpy(stream, d->aframe, MIN(len, d->aframelen));
299 d->aframeoff += MIN(len, d->aframelen);
300 stream += MIN(len, d->aframelen);
301 written += MIN(len, d->aframelen);
302 len -= MIN(len, d->aframelen);
305 av_free_packet(&packet);
308 return written;
311 static int ff_av_rewind(int id)
313 if (id >= datasz || !data[id].fmt)
314 return -1;
316 if (av_seek_frame(data[id].fmt, -1, 0, AVSEEK_FLAG_BYTE) < 0)
317 return -1;
319 return 0;
322 int init(plugin_useservice_t useservice)
324 static struct av_accessor av = {
325 .open = ff_av_open,
326 .close = ff_av_close,
327 .read_audio = ff_av_read_audio,
328 .rewind = ff_av_rewind,
331 if (useservice(LOG_SERVICE, FFMPEG_MODULE, &logger))
332 return -1;
334 if (useservice(VFSACCESS_SERVICE, FFMPEG_MODULE, &vfs))
335 return -1;
337 if (useservice(AV_SERVICE, FFMPEG_MODULE, &av))
338 return -1;
340 if (moreresources())
341 return -1;
343 av_register_all();
344 register_protocol(&vfs_protocol);
346 logger.write(LOG_INFO, FFMPEG_MODULE,
347 "libavformat/libavcodec input registered.");
349 return 0;