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
19 #define FFMPEG_MODULE "AV-FFmpeg"
20 #define FFMPEG_MOD_VERSION "0.1.0"
28 #include <libavformat/avformat.h>
29 #include <libavcodec/avcodec.h>
30 #include <libavutil/avstring.h>
32 #include "../plugin/plugin.h"
33 #include "../av/avfile.h"
34 #include "../vfs/vfs.h"
35 #include "../sys/log.h"
37 #define init ffmpeg_LTX_init
39 #define MIN(a,b) (((a)<(b))?(a):(b))
41 static struct resource
{
43 AVCodecContext
*actx
, *vctx
;
44 AVCodec
*acodec
, *vcodec
;
48 int aframelen
, aframeoff
;
50 struct audio_format afmt
;
52 static int datasz
= 4;
54 static struct vfs_access vfs
;
55 static struct log_context logger
;
57 /* 2ooM VFS open handler for libavformat */
58 static int ff_vfs_open(URLContext
*h
, const char *filename
, int flags
)
62 av_strstart(filename
, "vfs:", &filename
);
64 /* 2ooM VFS is read-only */
65 if (flags
& URL_RDWR
|| flags
& URL_WRONLY
)
68 if (!(f
= vfs
.open(filename
)))
76 static int ff_vfs_read(URLContext
*h
, unsigned char *buf
, int size
)
78 return vfs
.read(h
->priv_data
, buf
, size
);
81 static int ff_vfs_write(URLContext
*h
, unsigned char *buf
, int size
)
83 return -1; /* 2ooM VFS is read-only */
86 static offset_t
ff_vfs_seek(URLContext
*h
, offset_t pos
, int whence
)
88 return vfs
.seek(h
->priv_data
, pos
, whence
);
91 static int ff_vfs_close(URLContext
*h
)
93 vfs
.close(h
->priv_data
);
97 static URLProtocol vfs_protocol
= {
99 .url_open
= ff_vfs_open
,
100 .url_read
= ff_vfs_read
,
101 .url_write
= ff_vfs_write
,
102 .url_seek
= ff_vfs_seek
,
103 .url_close
= ff_vfs_close
,
106 /* Double the size of the buffer for storing resources */
107 static int moreresources(void) {
109 struct resource
*new;
112 if (!(new = realloc(data
, sizeof *data
* datasz
*2)))
117 for (i
= (!init
) ? init
= 1, 0 : datasz
; i
< datasz
*2; i
++)
125 /* Locate the first stream matching type, initialize codecs, and return the
128 static int getstream(enum CodecType type
, AVCodec
**codec
, AVFormatContext
*fmt
)
133 for (i
= 0; i
< fmt
->nb_streams
; i
++)
134 if (fmt
->streams
[i
]->codec
->codec_type
== type
)
137 if (i
>= fmt
->nb_streams
) {
138 logger
.write(LOG_DEBUG
, FFMPEG_MODULE
,
139 "Failed to find%s stream in %s",
140 (type
== CODEC_TYPE_AUDIO
) ? " audio" :
141 (type
== CODEC_TYPE_VIDEO
) ? " video" : "",
146 ctx
= fmt
->streams
[i
]->codec
;
148 if (!(*codec
= avcodec_find_decoder(ctx
->codec_id
))) {
149 logger
.write(LOG_DEBUG
, FFMPEG_MODULE
,
150 "Failed to find decoder for %s", ctx
->codec_name
);
154 if (avcodec_open(ctx
, *codec
) < 0) {
155 logger
.write(LOG_DEBUG
, FFMPEG_MODULE
,
156 "Failed to open decoder %s", (*codec
)->name
);
163 /* Attempt to open a VFS resource with FFmpeg */
164 static int ff_av_open(const char *resource
, int type
,
165 const struct audio_format
*afmt
,
166 const struct video_format
*vfmt
)
169 struct resource r
= { .astream
= -1, .vstream
= -1 };
172 if (!(type
& AV_INPUT_AUDIO
|| type
& AV_INPUT_VIDEO
))
175 if (!(url
= malloc(strlen("vfs:") + strlen(resource
) + 1)))
178 /* Find a free resource, or make one */
179 for (i
= 0; i
< datasz
; i
++)
180 if (data
[i
].fmt
== NULL
)
187 strcat(url
, resource
);
189 if (av_open_input_file(&r
.fmt
, url
, NULL
, 0, NULL
) != 0) {
190 logger
.write(LOG_DEBUG
, FFMPEG_MODULE
,
191 "Failed to open %s", url
);
195 if (av_find_stream_info(r
.fmt
) < 0) {
196 logger
.write(LOG_DEBUG
, FFMPEG_MODULE
,
197 "Failed to find stream info for %s", url
);
201 if (type
& AV_INPUT_AUDIO
) {
202 r
.astream
= getstream(CODEC_TYPE_AUDIO
, &r
.acodec
, r
.fmt
);
205 r
.actx
= r
.fmt
->streams
[r
.astream
]->codec
;
207 if (!(r
.aframe
= malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE
)))
210 if (type
& AV_INPUT_VIDEO
) {
211 r
.vstream
= getstream(CODEC_TYPE_VIDEO
, &r
.vcodec
, r
.fmt
);
214 r
.vctx
= r
.fmt
->streams
[r
.vstream
]->codec
;
221 avcodec_close(r
.actx
);
223 avcodec_close(r
.vctx
);
225 av_close_input_file(r
.fmt
);
233 /* Close an open resource */
234 static void ff_av_close(int id
)
236 if (id
>= datasz
|| !data
[id
].fmt
)
240 avcodec_close(data
[id
].actx
);
242 avcodec_close(data
[id
].vctx
);
244 av_close_input_file(data
[id
].fmt
);
247 free(data
[id
].aframe
);
252 /* Read decoded audio data */
253 static int ff_av_read_audio(int id
, void *buf
, int len
)
259 if (id
>= datasz
|| !data
[id
].fmt
|| data
[id
].astream
== -1)
264 if (d
->aframeoff
< d
->aframelen
) {
265 if (d
->aframelen
- d
->aframeoff
>= len
) {
266 memcpy(stream
, d
->aframe
+(d
->aframeoff
/2), len
);
271 memcpy(stream
, d
->aframe
+(d
->aframeoff
/2),
272 d
->aframelen
- d
->aframeoff
);
274 written
+= d
->aframelen
- d
->aframeoff
;
275 len
-= d
->aframelen
- d
->aframeoff
;
276 stream
+= d
->aframelen
- d
->aframeoff
;
282 d
->aframelen
= 0, d
->aframeoff
= 0;
283 if (av_read_frame(d
->fmt
, &packet
) < 0)
286 if (packet
.stream_index
== d
->astream
) {
287 d
->aframelen
= AVCODEC_MAX_AUDIO_FRAME_SIZE
;
288 avcodec_decode_audio2(d
->actx
, d
->aframe
, &d
->aframelen
,
289 packet
.data
, packet
.size
);
291 if (d
->aframelen
<= 0)
292 return av_free_packet(&packet
), written
;
294 memcpy(stream
, d
->aframe
, MIN(len
, d
->aframelen
));
296 d
->aframeoff
+= MIN(len
, d
->aframelen
);
297 stream
+= MIN(len
, d
->aframelen
);
298 written
+= MIN(len
, d
->aframelen
);
299 len
-= MIN(len
, d
->aframelen
);
302 av_free_packet(&packet
);
308 static int ff_av_rewind(int id
)
310 if (id
>= datasz
|| !data
[id
].fmt
)
313 if (av_seek_frame(data
[id
].fmt
, -1, 0, AVSEEK_FLAG_BYTE
) < 0)
319 int init(plugin_useservice_t useservice
)
321 static struct av_accessor av
= {
323 .close
= ff_av_close
,
324 .read_audio
= ff_av_read_audio
,
325 .rewind
= ff_av_rewind
,
328 if (useservice(LOG_SERVICE
, FFMPEG_MODULE
, &logger
))
331 if (useservice(VFSACCESS_SERVICE
, FFMPEG_MODULE
, &vfs
))
334 if (useservice(AV_PLUGIN_SERVICE
, FFMPEG_MODULE
, &av
))
341 register_protocol(&vfs_protocol
);
343 logger
.write(LOG_INFO
, FFMPEG_MODULE
,
344 "libavformat/libavcodec media input " FFMPEG_MOD_VERSION
);