1
by Sérgio Benjamim
FFmpeg 2.7.1 source for ppsspp. |
1 |
/*
|
2 |
* This file is part of FFmpeg.
|
|
3 |
*
|
|
4 |
* FFmpeg is free software; you can redistribute it and/or
|
|
5 |
* modify it under the terms of the GNU Lesser General Public
|
|
6 |
* License as published by the Free Software Foundation; either
|
|
7 |
* version 2.1 of the License, or (at your option) any later version.
|
|
8 |
*
|
|
9 |
* FFmpeg 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 GNU
|
|
12 |
* Lesser General Public License for more details.
|
|
13 |
*
|
|
14 |
* You should have received a copy of the GNU Lesser General Public
|
|
15 |
* License along with FFmpeg; if not, write to the Free Software
|
|
16 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
17 |
*/
|
|
18 |
||
19 |
/**
|
|
20 |
* @file
|
|
21 |
* ModPlug demuxer
|
|
22 |
* @todo better probing than extensions matching
|
|
23 |
*/
|
|
24 |
||
25 |
#define MODPLUG_STATIC
|
|
26 |
#include <libmodplug/modplug.h> |
|
27 |
#include "libavutil/avstring.h" |
|
28 |
#include "libavutil/eval.h" |
|
29 |
#include "libavutil/opt.h" |
|
30 |
#include "avformat.h" |
|
31 |
#include "internal.h" |
|
32 |
||
33 |
typedef struct ModPlugContext { |
|
34 |
const AVClass *class; |
|
35 |
ModPlugFile *f; |
|
36 |
uint8_t *buf; ///< input file content |
|
37 |
||
38 |
/* options */
|
|
39 |
int noise_reduction; |
|
40 |
int reverb_depth; |
|
41 |
int reverb_delay; |
|
42 |
int bass_amount; |
|
43 |
int bass_range; |
|
44 |
int surround_depth; |
|
45 |
int surround_delay; |
|
46 |
||
47 |
int max_size; ///< max file size to allocate |
|
48 |
||
49 |
/* optional video stream */
|
|
50 |
double ts_per_packet; ///< used to define the pts/dts using packet_count; |
|
51 |
int packet_count; ///< total number of audio packets |
|
52 |
int print_textinfo; ///< bool flag for printing speed, tempo, order, ... |
|
53 |
int video_stream; ///< 1 if the user want a video stream, otherwise 0 |
|
54 |
int w; ///< video stream width in char (one char = 8x8px) |
|
55 |
int h; ///< video stream height in char (one char = 8x8px) |
|
56 |
int video_switch; ///< 1 if current packet is video, otherwise 0 |
|
57 |
int fsize; ///< constant frame size |
|
58 |
int linesize; ///< line size in bytes |
|
59 |
char *color_eval; ///< color eval user input expression |
|
60 |
AVExpr *expr; ///< parsed color eval expression |
|
61 |
} ModPlugContext; |
|
62 |
||
63 |
static const char * const var_names[] = { |
|
64 |
"x", "y", |
|
65 |
"w", "h", |
|
66 |
"t", |
|
67 |
"speed", "tempo", "order", "pattern", "row", |
|
68 |
NULL
|
|
69 |
};
|
|
70 |
||
71 |
enum var_name { |
|
72 |
VAR_X, VAR_Y, |
|
73 |
VAR_W, VAR_H, |
|
74 |
VAR_TIME, |
|
75 |
VAR_SPEED, VAR_TEMPO, VAR_ORDER, VAR_PATTERN, VAR_ROW, |
|
76 |
VAR_VARS_NB
|
|
77 |
};
|
|
78 |
||
79 |
#define FF_MODPLUG_MAX_FILE_SIZE (100 * 1<<20) // 100M |
|
80 |
#define FF_MODPLUG_DEF_FILE_SIZE ( 5 * 1<<20) // 5M |
|
81 |
||
82 |
#define OFFSET(x) offsetof(ModPlugContext, x)
|
|
83 |
#define D AV_OPT_FLAG_DECODING_PARAM
|
|
84 |
static const AVOption options[] = { |
|
85 |
{"noise_reduction", "Enable noise reduction 0(off)-1(on)", OFFSET(noise_reduction), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D}, |
|
86 |
{"reverb_depth", "Reverb level 0(quiet)-100(loud)", OFFSET(reverb_depth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, |
|
87 |
{"reverb_delay", "Reverb delay in ms, usually 40-200ms", OFFSET(reverb_delay), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D}, |
|
88 |
{"bass_amount", "XBass level 0(quiet)-100(loud)", OFFSET(bass_amount), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, |
|
89 |
{"bass_range", "XBass cutoff in Hz 10-100", OFFSET(bass_range), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, |
|
90 |
{"surround_depth", "Surround level 0(quiet)-100(heavy)", OFFSET(surround_depth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 100, D}, |
|
91 |
{"surround_delay", "Surround delay in ms, usually 5-40ms", OFFSET(surround_delay), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, D}, |
|
92 |
{"max_size", "Max file size supported (in bytes). Default is 5MB. Set to 0 for no limit (not recommended)", |
|
93 |
OFFSET(max_size), AV_OPT_TYPE_INT, {.i64 = FF_MODPLUG_DEF_FILE_SIZE}, 0, FF_MODPLUG_MAX_FILE_SIZE, D}, |
|
94 |
{"video_stream_expr", "Color formula", OFFSET(color_eval), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, D}, |
|
95 |
{"video_stream", "Make demuxer output a video stream", OFFSET(video_stream), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D}, |
|
96 |
{"video_stream_w", "Video stream width in char (one char = 8x8px)", OFFSET(w), AV_OPT_TYPE_INT, {.i64 = 30}, 20, 512, D}, |
|
97 |
{"video_stream_h", "Video stream height in char (one char = 8x8px)", OFFSET(h), AV_OPT_TYPE_INT, {.i64 = 30}, 20, 512, D}, |
|
98 |
{"video_stream_ptxt", "Print speed, tempo, order, ... in video stream", OFFSET(print_textinfo), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, D}, |
|
99 |
{NULL}, |
|
100 |
};
|
|
101 |
||
102 |
#define SET_OPT_IF_REQUESTED(libopt, opt, flag) do { \
|
|
103 |
if (modplug->opt) { \
|
|
104 |
settings.libopt = modplug->opt; \
|
|
105 |
settings.mFlags |= flag; \
|
|
106 |
} \
|
|
107 |
} while (0)
|
|
108 |
||
109 |
#define ADD_META_MULTIPLE_ENTRIES(entry_name, fname) do { \
|
|
110 |
if (n_## entry_name ##s) { \
|
|
111 |
unsigned i, n = 0; \
|
|
112 |
\
|
|
113 |
for (i = 0; i < n_## entry_name ##s; i++) { \
|
|
114 |
char item_name[64] = {0}; \
|
|
115 |
fname(f, i, item_name); \
|
|
116 |
if (!*item_name) \
|
|
117 |
continue; \
|
|
118 |
if (n) \
|
|
119 |
av_dict_set(&s->metadata, #entry_name, "\n", AV_DICT_APPEND); \
|
|
120 |
av_dict_set(&s->metadata, #entry_name, item_name, AV_DICT_APPEND); \
|
|
121 |
n++; \
|
|
122 |
} \
|
|
123 |
\
|
|
124 |
extra = av_asprintf(", %u/%u " #entry_name "%s", \
|
|
125 |
n, n_## entry_name ##s, n > 1 ? "s" : ""); \
|
|
126 |
if (!extra) \
|
|
127 |
return AVERROR(ENOMEM); \
|
|
128 |
av_dict_set(&s->metadata, "extra info", extra, AV_DICT_APPEND); \
|
|
129 |
av_free(extra); \
|
|
130 |
} \
|
|
131 |
} while (0)
|
|
132 |
||
133 |
static int modplug_load_metadata(AVFormatContext *s) |
|
134 |
{
|
|
135 |
ModPlugContext *modplug = s->priv_data; |
|
136 |
ModPlugFile *f = modplug->f; |
|
137 |
char *extra; |
|
138 |
const char *name = ModPlug_GetName(f); |
|
139 |
const char *msg = ModPlug_GetMessage(f); |
|
140 |
||
141 |
unsigned n_instruments = ModPlug_NumInstruments(f); |
|
142 |
unsigned n_samples = ModPlug_NumSamples(f); |
|
143 |
unsigned n_patterns = ModPlug_NumPatterns(f); |
|
144 |
unsigned n_channels = ModPlug_NumChannels(f); |
|
145 |
||
146 |
if (name && *name) av_dict_set(&s->metadata, "name", name, 0); |
|
147 |
if (msg && *msg) av_dict_set(&s->metadata, "message", msg, 0); |
|
148 |
||
149 |
extra = av_asprintf("%u pattern%s, %u channel%s", |
|
150 |
n_patterns, n_patterns > 1 ? "s" : "", |
|
151 |
n_channels, n_channels > 1 ? "s" : ""); |
|
152 |
if (!extra) |
|
153 |
return AVERROR(ENOMEM); |
|
154 |
av_dict_set(&s->metadata, "extra info", extra, AV_DICT_DONT_STRDUP_VAL); |
|
155 |
||
156 |
ADD_META_MULTIPLE_ENTRIES(instrument, ModPlug_InstrumentName); |
|
157 |
ADD_META_MULTIPLE_ENTRIES(sample, ModPlug_SampleName); |
|
158 |
||
159 |
return 0; |
|
160 |
}
|
|
161 |
||
162 |
#define AUDIO_PKT_SIZE 512
|
|
163 |
||
164 |
static int modplug_read_header(AVFormatContext *s) |
|
165 |
{
|
|
166 |
AVStream *st; |
|
167 |
AVIOContext *pb = s->pb; |
|
168 |
ModPlug_Settings settings; |
|
169 |
ModPlugContext *modplug = s->priv_data; |
|
170 |
int64_t sz = avio_size(pb); |
|
171 |
||
172 |
if (sz < 0) { |
|
173 |
av_log(s, AV_LOG_WARNING, "Could not determine file size\n"); |
|
174 |
sz = modplug->max_size; |
|
175 |
} else if (modplug->max_size && sz > modplug->max_size) { |
|
176 |
sz = modplug->max_size; |
|
177 |
av_log(s, AV_LOG_WARNING, "Max file size reach%s, allocating %"PRIi64"B " |
|
178 |
"but demuxing is likely to fail due to incomplete buffer\n", |
|
179 |
sz == FF_MODPLUG_DEF_FILE_SIZE ? " (see -max_size)" : "", sz); |
|
180 |
}
|
|
181 |
||
182 |
if (modplug->color_eval) { |
|
183 |
int r = av_expr_parse(&modplug->expr, modplug->color_eval, var_names, |
|
184 |
NULL, NULL, NULL, NULL, 0, s); |
|
185 |
if (r < 0) |
|
186 |
return r; |
|
187 |
}
|
|
188 |
||
189 |
modplug->buf = av_malloc(modplug->max_size); |
|
190 |
if (!modplug->buf) |
|
191 |
return AVERROR(ENOMEM); |
|
192 |
sz = avio_read(pb, modplug->buf, sz); |
|
193 |
||
194 |
ModPlug_GetSettings(&settings); |
|
195 |
settings.mChannels = 2; |
|
196 |
settings.mBits = 16; |
|
197 |
settings.mFrequency = 44100; |
|
198 |
settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; // best quality |
|
199 |
settings.mLoopCount = 0; // prevents looping forever |
|
200 |
||
201 |
if (modplug->noise_reduction) settings.mFlags |= MODPLUG_ENABLE_NOISE_REDUCTION; |
|
202 |
SET_OPT_IF_REQUESTED(mReverbDepth, reverb_depth, MODPLUG_ENABLE_REVERB); |
|
203 |
SET_OPT_IF_REQUESTED(mReverbDelay, reverb_delay, MODPLUG_ENABLE_REVERB); |
|
204 |
SET_OPT_IF_REQUESTED(mBassAmount, bass_amount, MODPLUG_ENABLE_MEGABASS); |
|
205 |
SET_OPT_IF_REQUESTED(mBassRange, bass_range, MODPLUG_ENABLE_MEGABASS); |
|
206 |
SET_OPT_IF_REQUESTED(mSurroundDepth, surround_depth, MODPLUG_ENABLE_SURROUND); |
|
207 |
SET_OPT_IF_REQUESTED(mSurroundDelay, surround_delay, MODPLUG_ENABLE_SURROUND); |
|
208 |
||
209 |
if (modplug->reverb_depth) settings.mReverbDepth = modplug->reverb_depth; |
|
210 |
if (modplug->reverb_delay) settings.mReverbDelay = modplug->reverb_delay; |
|
211 |
if (modplug->bass_amount) settings.mBassAmount = modplug->bass_amount; |
|
212 |
if (modplug->bass_range) settings.mBassRange = modplug->bass_range; |
|
213 |
if (modplug->surround_depth) settings.mSurroundDepth = modplug->surround_depth; |
|
214 |
if (modplug->surround_delay) settings.mSurroundDelay = modplug->surround_delay; |
|
215 |
||
216 |
ModPlug_SetSettings(&settings); |
|
217 |
||
218 |
modplug->f = ModPlug_Load(modplug->buf, sz); |
|
219 |
if (!modplug->f) |
|
220 |
return AVERROR_INVALIDDATA; |
|
221 |
||
222 |
st = avformat_new_stream(s, NULL); |
|
223 |
if (!st) |
|
224 |
return AVERROR(ENOMEM); |
|
225 |
avpriv_set_pts_info(st, 64, 1, 1000); |
|
226 |
st->duration = ModPlug_GetLength(modplug->f); |
|
227 |
st->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
|
228 |
st->codec->codec_id = AV_CODEC_ID_PCM_S16LE; |
|
229 |
st->codec->channels = settings.mChannels; |
|
230 |
st->codec->sample_rate = settings.mFrequency; |
|
231 |
||
232 |
// timebase = 1/1000, 2ch 16bits 44.1kHz-> 2*2*44100
|
|
233 |
modplug->ts_per_packet = 1000*AUDIO_PKT_SIZE / (4*44100.); |
|
234 |
||
235 |
if (modplug->video_stream) { |
|
236 |
AVStream *vst = avformat_new_stream(s, NULL); |
|
237 |
if (!vst) |
|
238 |
return AVERROR(ENOMEM); |
|
239 |
avpriv_set_pts_info(vst, 64, 1, 1000); |
|
240 |
vst->duration = st->duration; |
|
241 |
vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
|
242 |
vst->codec->codec_id = AV_CODEC_ID_XBIN; |
|
243 |
vst->codec->width = modplug->w << 3; |
|
244 |
vst->codec->height = modplug->h << 3; |
|
245 |
modplug->linesize = modplug->w * 3; |
|
246 |
modplug->fsize = modplug->linesize * modplug->h; |
|
247 |
}
|
|
248 |
||
249 |
return modplug_load_metadata(s); |
|
250 |
}
|
|
251 |
||
252 |
static void write_text(uint8_t *dst, const char *s, int linesize, int x, int y) |
|
253 |
{
|
|
254 |
int i; |
|
255 |
dst += y*linesize + x*3; |
|
256 |
for (i = 0; s[i]; i++, dst += 3) { |
|
257 |
dst[0] = 0x0; // count - 1 |
|
258 |
dst[1] = s[i]; // char |
|
259 |
dst[2] = 0x0f; // background / foreground |
|
260 |
}
|
|
261 |
}
|
|
262 |
||
263 |
#define PRINT_INFO(line, name, idvalue) do { \
|
|
264 |
snprintf(intbuf, sizeof(intbuf), "%.0f", var_values[idvalue]); \
|
|
265 |
write_text(pkt->data, name ":", modplug->linesize, 0+1, line+1); \
|
|
266 |
write_text(pkt->data, intbuf, modplug->linesize, 10+1, line+1); \
|
|
267 |
} while (0)
|
|
268 |
||
269 |
static int modplug_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
270 |
{
|
|
271 |
ModPlugContext *modplug = s->priv_data; |
|
272 |
||
273 |
if (modplug->video_stream) { |
|
274 |
modplug->video_switch ^= 1; // one video packet for one audio packet |
|
275 |
if (modplug->video_switch) { |
|
276 |
double var_values[VAR_VARS_NB]; |
|
277 |
||
278 |
var_values[VAR_W ] = modplug->w; |
|
279 |
var_values[VAR_H ] = modplug->h; |
|
280 |
var_values[VAR_TIME ] = modplug->packet_count * modplug->ts_per_packet; |
|
281 |
var_values[VAR_SPEED ] = ModPlug_GetCurrentSpeed (modplug->f); |
|
282 |
var_values[VAR_TEMPO ] = ModPlug_GetCurrentTempo (modplug->f); |
|
283 |
var_values[VAR_ORDER ] = ModPlug_GetCurrentOrder (modplug->f); |
|
284 |
var_values[VAR_PATTERN] = ModPlug_GetCurrentPattern(modplug->f); |
|
285 |
var_values[VAR_ROW ] = ModPlug_GetCurrentRow (modplug->f); |
|
286 |
||
287 |
if (av_new_packet(pkt, modplug->fsize) < 0) |
|
288 |
return AVERROR(ENOMEM); |
|
289 |
pkt->stream_index = 1; |
|
290 |
memset(pkt->data, 0, modplug->fsize); |
|
291 |
||
292 |
if (modplug->print_textinfo) { |
|
293 |
char intbuf[32]; |
|
294 |
PRINT_INFO(0, "speed", VAR_SPEED); |
|
295 |
PRINT_INFO(1, "tempo", VAR_TEMPO); |
|
296 |
PRINT_INFO(2, "order", VAR_ORDER); |
|
297 |
PRINT_INFO(3, "pattern", VAR_PATTERN); |
|
298 |
PRINT_INFO(4, "row", VAR_ROW); |
|
299 |
PRINT_INFO(5, "ts", VAR_TIME); |
|
300 |
}
|
|
301 |
||
302 |
if (modplug->expr) { |
|
303 |
int x, y; |
|
304 |
for (y = 0; y < modplug->h; y++) { |
|
305 |
for (x = 0; x < modplug->w; x++) { |
|
306 |
double color; |
|
307 |
var_values[VAR_X] = x; |
|
308 |
var_values[VAR_Y] = y; |
|
309 |
color = av_expr_eval(modplug->expr, var_values, NULL); |
|
310 |
pkt->data[y*modplug->linesize + x*3 + 2] |= av_clip((int)color, 0, 0xf)<<4; |
|
311 |
}
|
|
312 |
}
|
|
313 |
}
|
|
314 |
pkt->pts = pkt->dts = var_values[VAR_TIME]; |
|
315 |
pkt->flags |= AV_PKT_FLAG_KEY; |
|
316 |
return 0; |
|
317 |
}
|
|
318 |
}
|
|
319 |
||
320 |
if (av_new_packet(pkt, AUDIO_PKT_SIZE) < 0) |
|
321 |
return AVERROR(ENOMEM); |
|
322 |
||
323 |
if (modplug->video_stream) |
|
324 |
pkt->pts = pkt->dts = modplug->packet_count++ * modplug->ts_per_packet; |
|
325 |
||
326 |
pkt->size = ModPlug_Read(modplug->f, pkt->data, AUDIO_PKT_SIZE); |
|
327 |
if (pkt->size <= 0) { |
|
328 |
av_free_packet(pkt); |
|
329 |
return pkt->size == 0 ? AVERROR_EOF : AVERROR(EIO); |
|
330 |
}
|
|
331 |
return 0; |
|
332 |
}
|
|
333 |
||
334 |
static int modplug_read_close(AVFormatContext *s) |
|
335 |
{
|
|
336 |
ModPlugContext *modplug = s->priv_data; |
|
337 |
ModPlug_Unload(modplug->f); |
|
338 |
av_freep(&modplug->buf); |
|
339 |
return 0; |
|
340 |
}
|
|
341 |
||
342 |
static int modplug_read_seek(AVFormatContext *s, int stream_idx, int64_t ts, int flags) |
|
343 |
{
|
|
344 |
ModPlugContext *modplug = s->priv_data; |
|
345 |
ModPlug_Seek(modplug->f, (int)ts); |
|
346 |
if (modplug->video_stream) |
|
347 |
modplug->packet_count = ts / modplug->ts_per_packet; |
|
348 |
return 0; |
|
349 |
}
|
|
350 |
||
351 |
static const char modplug_extensions[] = "669,abc,amf,ams,dbm,dmf,dsm,far,it,mdl,med,mid,mod,mt2,mtm,okt,psm,ptm,s3m,stm,ult,umx,xm,itgz,itr,itz,mdgz,mdr,mdz,s3gz,s3r,s3z,xmgz,xmr,xmz"; |
|
352 |
||
353 |
static int modplug_probe(AVProbeData *p) |
|
354 |
{
|
|
355 |
if (av_match_ext(p->filename, modplug_extensions)) { |
|
356 |
if (p->buf_size < 16384) |
|
357 |
return AVPROBE_SCORE_EXTENSION/2-1; |
|
358 |
else
|
|
359 |
return AVPROBE_SCORE_EXTENSION; |
|
360 |
}
|
|
361 |
return 0; |
|
362 |
}
|
|
363 |
||
364 |
static const AVClass modplug_class = { |
|
365 |
.class_name = "ModPlug demuxer", |
|
366 |
.item_name = av_default_item_name, |
|
367 |
.option = options, |
|
368 |
.version = LIBAVUTIL_VERSION_INT, |
|
369 |
};
|
|
370 |
||
371 |
AVInputFormat ff_libmodplug_demuxer = { |
|
372 |
.name = "libmodplug", |
|
373 |
.long_name = NULL_IF_CONFIG_SMALL("ModPlug demuxer"), |
|
374 |
.priv_data_size = sizeof(ModPlugContext), |
|
375 |
.read_probe = modplug_probe, |
|
376 |
.read_header = modplug_read_header, |
|
377 |
.read_packet = modplug_read_packet, |
|
378 |
.read_close = modplug_read_close, |
|
379 |
.read_seek = modplug_read_seek, |
|
380 |
.extensions = modplug_extensions, |
|
381 |
.priv_class = &modplug_class, |
|
382 |
};
|