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
24 #define FFMPEG_MODULE "AV-FFmpeg"
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
{
46 AVCodecContext
*actx
, *vctx
;
47 AVCodec
*acodec
, *vcodec
;
51 int aframelen
, aframeoff
;
53 struct audio_format afmt
;
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
)
65 strstart(filename
, "vfs:", &filename
);
67 /* 2ooM VFS is read-only */
68 if (flags
& URL_RDWR
|| flags
& URL_WRONLY
)
71 if (!(f
= vfs
.open(filename
)))
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
);
100 static URLProtocol vfs_protocol
= {
110 /* Double the size of the buffer for storing resources */
111 static int moreresources(void) {
113 struct resource
*new;
116 if (!(new = realloc(data
, sizeof *data
* datasz
*2)))
121 for (i
= (!init
) ? init
= 1, 0 : datasz
; i
< datasz
*2; i
++)
129 /* Locate the first stream matching type, initialize codecs, and return the
132 static int getstream(enum CodecType type
, AVCodec
**codec
, AVFormatContext
*fmt
)
137 for (i
= 0; i
< fmt
->nb_streams
; i
++)
138 if (fmt
->streams
[i
]->codec
->codec_type
== type
)
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" : "",
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
);
158 if (avcodec_open(ctx
, *codec
) < 0) {
159 logger
.write(LOG_DEBUG
, FFMPEG_MODULE
,
160 "Failed to open decoder %s", (*codec
)->name
);
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
)
173 struct resource r
= { .astream
= -1, .vstream
= -1 };
176 if (!(type
& AV_INPUT_AUDIO
|| type
& AV_INPUT_VIDEO
))
179 if (!(url
= malloc(strlen("vfs:") + strlen(resource
) + 1)))
182 /* Find a free resource, or make one */
183 for (i
= 0; i
< datasz
; i
++)
184 if (data
[i
].fmt
== NULL
)
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
);
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
);
205 if (type
& AV_INPUT_AUDIO
) {
206 r
.astream
= getstream(CODEC_TYPE_AUDIO
, &r
.acodec
, r
.fmt
);
209 r
.actx
= r
.fmt
->streams
[r
.astream
]->codec
;
211 if (!(r
.aframe
= malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE
)))
214 if (type
& AV_INPUT_VIDEO
) {
215 r
.vstream
= getstream(CODEC_TYPE_VIDEO
, &r
.vcodec
, r
.fmt
);
218 r
.vctx
= r
.fmt
->streams
[r
.vstream
]->codec
;
225 avcodec_close(r
.actx
);
227 avcodec_close(r
.vctx
);
229 av_close_input_file(r
.fmt
);
237 /* Close an open resource */
238 static void ff_av_close(int id
)
240 if (id
>= datasz
|| !data
[id
].fmt
)
244 avcodec_close(data
[id
].actx
);
246 avcodec_close(data
[id
].vctx
);
248 av_close_input_file(data
[id
].fmt
);
251 free(data
[id
].aframe
);
256 /* Read decoded audio data */
257 static int ff_av_read_audio(int id
, void *buf
, int len
)
263 if (id
>= datasz
|| !data
[id
].fmt
|| data
[id
].astream
== -1)
268 if (d
->aframeoff
< d
->aframelen
) {
269 if (d
->aframelen
- d
->aframeoff
>= len
) {
270 memcpy(stream
, d
->aframe
+(d
->aframeoff
/2), len
);
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
;
286 d
->aframelen
= 0, d
->aframeoff
= 0;
287 if (av_read_frame(d
->fmt
, &packet
) < 0)
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
);
311 static int ff_av_rewind(int id
)
313 if (id
>= datasz
|| !data
[id
].fmt
)
316 if (av_seek_frame(data
[id
].fmt
, -1, 0, AVSEEK_FLAG_BYTE
) < 0)
322 int init(plugin_useservice_t useservice
)
324 static struct av_accessor av
= {
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
))
334 if (useservice(VFSACCESS_SERVICE
, FFMPEG_MODULE
, &vfs
))
337 if (useservice(AV_SERVICE
, FFMPEG_MODULE
, &av
))
344 register_protocol(&vfs_protocol
);
346 logger
.write(LOG_INFO
, FFMPEG_MODULE
,
347 "libavformat/libavcodec input registered.");