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"
25 #define FFMPEG_MOD_VERSION "0.1.0"
33 #include <ffmpeg/avformat.h>
34 #include <ffmpeg/avcodec.h>
36 #include "../plugin/plugin.h"
37 #include "../av/avfile.h"
38 #include "../vfs/vfs.h"
39 #include "../sys/log.h"
41 #define init ffmpeg_LTX_init
43 #define MIN(a,b) (((a)<(b))?(a):(b))
45 static struct resource
{
47 AVCodecContext
*actx
, *vctx
;
48 AVCodec
*acodec
, *vcodec
;
52 int aframelen
, aframeoff
;
54 struct audio_format afmt
;
56 static int datasz
= 4;
58 static struct vfs_access vfs
;
59 static struct log_context logger
;
61 /* 2ooM VFS open handler for libavformat */
62 static int ff_vfs_open(URLContext
*h
, const char *filename
, int flags
)
66 strstart(filename
, "vfs:", &filename
);
68 /* 2ooM VFS is read-only */
69 if (flags
& URL_RDWR
|| flags
& URL_WRONLY
)
72 if (!(f
= vfs
.open(filename
)))
80 static int ff_vfs_read(URLContext
*h
, unsigned char *buf
, int size
)
82 return vfs
.read(h
->priv_data
, buf
, size
);
85 static int ff_vfs_write(URLContext
*h
, unsigned char *buf
, int size
)
87 return -1; /* 2ooM VFS is read-only */
90 static offset_t
ff_vfs_seek(URLContext
*h
, offset_t pos
, int whence
)
92 return vfs
.seek(h
->priv_data
, pos
, whence
);
95 static int ff_vfs_close(URLContext
*h
)
97 vfs
.close(h
->priv_data
);
101 static URLProtocol vfs_protocol
= {
111 /* Double the size of the buffer for storing resources */
112 static int moreresources(void) {
114 struct resource
*new;
117 if (!(new = realloc(data
, sizeof *data
* datasz
*2)))
122 for (i
= (!init
) ? init
= 1, 0 : datasz
; i
< datasz
*2; i
++)
130 /* Locate the first stream matching type, initialize codecs, and return the
133 static int getstream(enum CodecType type
, AVCodec
**codec
, AVFormatContext
*fmt
)
138 for (i
= 0; i
< fmt
->nb_streams
; i
++)
139 if (fmt
->streams
[i
]->codec
->codec_type
== type
)
142 if (i
>= fmt
->nb_streams
) {
143 logger
.write(LOG_DEBUG
, FFMPEG_MODULE
,
144 "Failed to find%s stream in %s",
145 (type
== CODEC_TYPE_AUDIO
) ? " audio" :
146 (type
== CODEC_TYPE_VIDEO
) ? " video" : "",
151 ctx
= fmt
->streams
[i
]->codec
;
153 if (!(*codec
= avcodec_find_decoder(ctx
->codec_id
))) {
154 logger
.write(LOG_DEBUG
, FFMPEG_MODULE
,
155 "Failed to find decoder for %s", ctx
->codec_name
);
159 if (avcodec_open(ctx
, *codec
) < 0) {
160 logger
.write(LOG_DEBUG
, FFMPEG_MODULE
,
161 "Failed to open decoder %s", (*codec
)->name
);
168 /* Attempt to open a VFS resource with FFmpeg */
169 static int ff_av_open(const char *resource
, int type
,
170 const struct audio_format
*afmt
,
171 const struct video_format
*vfmt
)
174 struct resource r
= { .astream
= -1, .vstream
= -1 };
177 if (!(type
& AV_INPUT_AUDIO
|| type
& AV_INPUT_VIDEO
))
180 if (!(url
= malloc(strlen("vfs:") + strlen(resource
) + 1)))
183 /* Find a free resource, or make one */
184 for (i
= 0; i
< datasz
; i
++)
185 if (data
[i
].fmt
== NULL
)
192 strcat(url
, resource
);
194 if (av_open_input_file(&r
.fmt
, url
, NULL
, 0, NULL
) != 0) {
195 logger
.write(LOG_DEBUG
, FFMPEG_MODULE
,
196 "Failed to open %s", url
);
200 if (av_find_stream_info(r
.fmt
) < 0) {
201 logger
.write(LOG_DEBUG
, FFMPEG_MODULE
,
202 "Failed to find stream info for %s", url
);
206 if (type
& AV_INPUT_AUDIO
) {
207 r
.astream
= getstream(CODEC_TYPE_AUDIO
, &r
.acodec
, r
.fmt
);
210 r
.actx
= r
.fmt
->streams
[r
.astream
]->codec
;
212 if (!(r
.aframe
= malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE
)))
215 if (type
& AV_INPUT_VIDEO
) {
216 r
.vstream
= getstream(CODEC_TYPE_VIDEO
, &r
.vcodec
, r
.fmt
);
219 r
.vctx
= r
.fmt
->streams
[r
.vstream
]->codec
;
226 avcodec_close(r
.actx
);
228 avcodec_close(r
.vctx
);
230 av_close_input_file(r
.fmt
);
238 /* Close an open resource */
239 static void ff_av_close(int id
)
241 if (id
>= datasz
|| !data
[id
].fmt
)
245 avcodec_close(data
[id
].actx
);
247 avcodec_close(data
[id
].vctx
);
249 av_close_input_file(data
[id
].fmt
);
252 free(data
[id
].aframe
);
257 /* Read decoded audio data */
258 static int ff_av_read_audio(int id
, void *buf
, int len
)
264 if (id
>= datasz
|| !data
[id
].fmt
|| data
[id
].astream
== -1)
269 if (d
->aframeoff
< d
->aframelen
) {
270 if (d
->aframelen
- d
->aframeoff
>= len
) {
271 memcpy(stream
, d
->aframe
+(d
->aframeoff
/2), len
);
276 memcpy(stream
, d
->aframe
+(d
->aframeoff
/2),
277 d
->aframelen
- d
->aframeoff
);
279 written
+= d
->aframelen
- d
->aframeoff
;
280 len
-= d
->aframelen
- d
->aframeoff
;
281 stream
+= d
->aframelen
- d
->aframeoff
;
287 d
->aframelen
= 0, d
->aframeoff
= 0;
288 if (av_read_frame(d
->fmt
, &packet
) < 0)
291 if (packet
.stream_index
== d
->astream
) {
292 d
->aframelen
= AVCODEC_MAX_AUDIO_FRAME_SIZE
;
293 avcodec_decode_audio2(d
->actx
, d
->aframe
, &d
->aframelen
,
294 packet
.data
, packet
.size
);
296 if (d
->aframelen
<= 0)
297 return av_free_packet(&packet
), written
;
299 memcpy(stream
, d
->aframe
, MIN(len
, d
->aframelen
));
301 d
->aframeoff
+= MIN(len
, d
->aframelen
);
302 stream
+= MIN(len
, d
->aframelen
);
303 written
+= MIN(len
, d
->aframelen
);
304 len
-= MIN(len
, d
->aframelen
);
307 av_free_packet(&packet
);
313 static int ff_av_rewind(int id
)
315 if (id
>= datasz
|| !data
[id
].fmt
)
318 if (av_seek_frame(data
[id
].fmt
, -1, 0, AVSEEK_FLAG_BYTE
) < 0)
324 int init(plugin_useservice_t useservice
)
326 static struct av_accessor av
= {
328 .close
= ff_av_close
,
329 .read_audio
= ff_av_read_audio
,
330 .rewind
= ff_av_rewind
,
333 if (useservice(LOG_SERVICE
, FFMPEG_MODULE
, &logger
))
336 if (useservice(VFSACCESS_SERVICE
, FFMPEG_MODULE
, &vfs
))
339 if (useservice(AV_PLUGIN_SERVICE
, FFMPEG_MODULE
, &av
))
346 register_protocol(&vfs_protocol
);
348 logger
.write(LOG_INFO
, FFMPEG_MODULE
,
349 "libavformat/libavcodec media input " FFMPEG_MOD_VERSION
);