FFmpeg
Loading...
Searching...
No Matches
libopenmpt.c
Go to the documentation of this file.
1/*
2 * Tracked MOD demuxer (libopenmpt)
3 * Copyright (c) 2016 Josh de Kock
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <libopenmpt/libopenmpt.h>
23#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
24#include <libopenmpt/libopenmpt_version.h>
25/* Shims to support libopenmpt < 0.3.0 (as documented by libopenmpt) */
26#if !defined(OPENMPT_API_VERSION_MAKE)
27#define OPENMPT_API_VERSION_MAKE(major, minor, patch) (((major)<<24)|((minor)<<16)|((patch)<<0))
28#endif
29#if !defined(OPENMPT_API_VERSION_AT_LEAST)
30#define OPENMPT_API_VERSION_AT_LEAST(major, minor, patch) (OPENMPT_API_VERSION >= OPENMPT_API_VERSION_MAKE((major), (minor), (patch)))
31#endif
32
34#include "libavutil/mem.h"
35#include "libavutil/opt.h"
36#include "avformat.h"
37#include "demux.h"
38#include "internal.h"
39
40typedef struct OpenMPTContext {
41 const AVClass *class;
42 openmpt_module *module;
43
44 double duration;
45 /* options */
49 int interp;
51
52#define OFFSET(x) offsetof(OpenMPTContext, x)
53#define A AV_OPT_FLAG_AUDIO_PARAM
54#define D AV_OPT_FLAG_DECODING_PARAM
55static const AVOption options[] = {
56 { "sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, { .i64 = 48000 }, 1000, INT_MAX, A | D },
57 { "layout", "set channel layout", OFFSET(ch_layout), AV_OPT_TYPE_CHLAYOUT, { .str = "stereo" }, 0, 0, A | D },
58 { "subsong", "set subsong", OFFSET(subsong), AV_OPT_TYPE_INT, { .i64 = -2 }, -2, INT_MAX, A | D, .unit = "subsong"},
59 { "all", "all", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, A | D, .unit = "subsong" },
60 { "auto", "auto", 0, AV_OPT_TYPE_CONST, { .i64 = -2 }, 0, 0, A | D, .unit = "subsong" },
61 { "interpolation", "set interpolation method", OFFSET(interp), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, A | D, .unit = "interpolation"},
62 { "default", "default", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, A | D, .unit = "interpolation"},
63 { "none", "none", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, A | D, .unit = "interpolation"},
64 { "linear", "linear", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, A | D, .unit = "interpolation"},
65 { "cubic", "cubic", 0, AV_OPT_TYPE_CONST, { .i64 = 4 }, 0, 0, A | D, .unit = "interpolation"},
66 { "8-tap-sinc", "8-tap windowed sinc", 0, AV_OPT_TYPE_CONST, { .i64 = 8 }, 0, 0, A | D, .unit = "interpolation"},
67 { NULL }
68};
69
70static void openmpt_logfunc(const char *message, void *userdata)
71{
72 int level = AV_LOG_INFO;
73 if (strstr(message, "ERROR") != NULL) {
75 }
76 av_log(userdata, level, "%s\n", message);
77}
78
79#define add_meta(s, name, meta) \
80do { \
81 const char *value = meta; \
82 if (value && value[0]) \
83 av_dict_set(&s->metadata, name, value, 0); \
84 openmpt_free_string(value); \
85} while(0)
86
88{
89 AVStream *st;
90 OpenMPTContext *openmpt = s->priv_data;
92 char *buf;
93#if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
94 int error;
95#endif
96 int ret;
97
98 size = avio_size(s->pb);
99 if (size <= 0)
100 return AVERROR_INVALIDDATA;
101 buf = av_malloc(size);
102 if (!buf)
103 return AVERROR(ENOMEM);
104 size = avio_read(s->pb, buf, size);
105 if (size < 0) {
106 av_log(s, AV_LOG_ERROR, "Reading input buffer failed.\n");
107 av_freep(&buf);
108 return size;
109 }
110
111#if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
112 error = OPENMPT_ERROR_OK;
113 openmpt->module = openmpt_module_create_from_memory2(buf, size, openmpt_logfunc, s, NULL, NULL, &error, NULL, NULL);
114 av_freep(&buf);
115 if (!openmpt->module) {
116 if (error == OPENMPT_ERROR_OUT_OF_MEMORY)
117 return AVERROR(ENOMEM);
118 else if (error >= OPENMPT_ERROR_GENERAL)
119 return AVERROR_INVALIDDATA;
120 else
121 return AVERROR_UNKNOWN;
122 }
123#else
124 openmpt->module = openmpt_module_create_from_memory(buf, size, openmpt_logfunc, s, NULL);
125 av_freep(&buf);
126 if (!openmpt->module)
127 return AVERROR_INVALIDDATA;
128#endif
129
130 ret = openmpt_module_set_render_param(openmpt->module,
131 OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH,
132 openmpt->interp);
133 if (!ret) {
134 av_log(s, AV_LOG_ERROR, "Invalid interpolation setting: %d\n", openmpt->interp);
135 return AVERROR(EINVAL);
136 }
137
138 if (openmpt->subsong >= openmpt_module_get_num_subsongs(openmpt->module)) {
139 av_log(s, AV_LOG_ERROR, "Invalid subsong index: %d\n", openmpt->subsong);
140 return AVERROR(EINVAL);
141 }
142
143 if (openmpt->subsong != -2) {
144 if (openmpt->subsong >= 0) {
145 av_dict_set_int(&s->metadata, "track", openmpt->subsong + 1, 0);
146 }
147 ret = openmpt_module_select_subsong(openmpt->module, openmpt->subsong);
148 if (!ret){
149 av_log(s, AV_LOG_ERROR, "Could not select requested subsong: %d", openmpt->subsong);
150 return AVERROR(EINVAL);
151 }
152 }
153
154 openmpt->duration = openmpt_module_get_duration_seconds(openmpt->module);
155
156 add_meta(s, "artist", openmpt_module_get_metadata(openmpt->module, "artist"));
157 add_meta(s, "title", openmpt_module_get_metadata(openmpt->module, "title"));
158 add_meta(s, "encoder", openmpt_module_get_metadata(openmpt->module, "tracker"));
159 add_meta(s, "comment", openmpt_module_get_metadata(openmpt->module, "message"));
160 add_meta(s, "date", openmpt_module_get_metadata(openmpt->module, "date"));
161
163 if (!st)
164 return AVERROR(ENOMEM);
166 if (openmpt->duration >= 0 && openmpt->duration < ((double)INT64_MAX + 1) / AV_TIME_BASE)
167 st->duration = llrint(openmpt->duration*AV_TIME_BASE);
168
171 st->codecpar->sample_rate = openmpt->sample_rate;
173 if (ret < 0)
174 return ret;
175
176 return 0;
177}
178
179#define AUDIO_PKT_SIZE 2048
180
182{
183 OpenMPTContext *openmpt = s->priv_data;
184 int n_samples = AUDIO_PKT_SIZE / (openmpt->ch_layout.nb_channels ? openmpt->ch_layout.nb_channels*4 : 4);
185 int ret;
186
187 if ((ret = av_new_packet(pkt, AUDIO_PKT_SIZE)) < 0)
188 return ret;
189
190 double pos = openmpt_module_get_position_seconds(openmpt->module);
191
192 switch (openmpt->ch_layout.nb_channels) {
193 case 1:
194 ret = openmpt_module_read_float_mono(openmpt->module, openmpt->sample_rate,
195 n_samples, (float *)pkt->data);
196 break;
197 case 2:
198 ret = openmpt_module_read_interleaved_float_stereo(openmpt->module, openmpt->sample_rate,
199 n_samples, (float *)pkt->data);
200 break;
201 case 4:
202 ret = openmpt_module_read_interleaved_float_quad(openmpt->module, openmpt->sample_rate,
203 n_samples, (float *)pkt->data);
204 break;
205 default:
206 av_log(s, AV_LOG_ERROR, "Unsupported number of channels: %d", openmpt->ch_layout.nb_channels);
207 return AVERROR(EINVAL);
208 }
209
210 if (ret < 1) {
211 pkt->size = 0;
212 return AVERROR_EOF;
213 }
214
215 pkt->size = ret * (openmpt->ch_layout.nb_channels * 4);
216
217 if (pos >= 0 && pos < ((double)INT64_MAX + 1) / AV_TIME_BASE)
218 pkt->pts = llrint(pos * AV_TIME_BASE);
219
220 return 0;
221}
222
224{
225 OpenMPTContext *openmpt = s->priv_data;
226 if (openmpt->module) {
227 openmpt_module_destroy(openmpt->module);
228 openmpt->module = NULL;
229 }
230 return 0;
231}
232
233static int read_seek_openmpt(AVFormatContext *s, int stream_idx, int64_t ts, int flags)
234{
235 OpenMPTContext *openmpt = s->priv_data;
236 if (ts < 0)
237 ts = 0;
238 openmpt_module_set_position_seconds(openmpt->module, (double)ts/AV_TIME_BASE);
239 return 0;
240}
241
243{
244 const char *ext;
245 if (p->filename) {
246 ext = strrchr(p->filename, '.');
247 if (ext && strlen(ext + 1) > 0) {
248 ext++; /* skip '.' */
249 if (openmpt_is_extension_supported(ext) == 1)
251 }
252 }
253 return 0;
254}
255
256static int read_probe_openmpt(const AVProbeData *p)
257{
258#if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
259 int probe_result;
260 if (p->buf && p->buf_size > 0) {
261 probe_result = openmpt_probe_file_header_without_filesize(
262 OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT,
263 p->buf, p->buf_size,
265 if (probe_result == OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS) {
266 /* As probing here relies on code external to FFmpeg, do not return
267 * AVPROBE_SCORE_MAX in order to reduce the impact in the rare
268 * cases of false positives.
269 */
270 return (AVPROBE_SCORE_MAX * 3) / 4 + 1;
271 } else if (probe_result == OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA) {
272 if (probe_openmpt_extension(p) > 0) {
273 return AVPROBE_SCORE_RETRY;
274 } else {
275 if (p->buf_size >= openmpt_probe_file_header_get_recommended_size()) {
276 /* We have already received the recommended amount of data
277 * and still cannot decide. Return a rather low score.
278 */
279 return AVPROBE_SCORE_RETRY / 2;
280 } else {
281 /* The file extension is unknown and we have very few data
282 * bytes available. libopenmpt cannot decide anything here,
283 * and returning any score > 0 would result in successful
284 * probing of random data.
285 */
286 return 0;
287 }
288 }
289 } else if (probe_result == OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE) {
290 return 0;
291 }
292 }
293#endif
294 /* for older libopenmpt, fall back to file extension probing */
295 return probe_openmpt_extension(p);
296}
297
298static const AVClass class_openmpt = {
299 .class_name = "libopenmpt",
300 .item_name = av_default_item_name,
301 .option = options,
302 .version = LIBAVUTIL_VERSION_INT,
303};
304
306 .p.name = "libopenmpt",
307 .p.long_name = NULL_IF_CONFIG_SMALL("Tracker formats (libopenmpt)"),
308 .p.priv_class = &class_openmpt,
309#if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
310 .p.extensions = "669,amf,ams,dbm,digi,dmf,dsm,dtm,far,gdm,ice,imf,it,j2b,m15,mdl,med,mmcmp,mms,mo3,mod,mptm,mt2,mtm,nst,okt,plm,ppm,psm,pt36,ptm,s3m,sfx,sfx2,st26,stk,stm,stp,ult,umx,wow,xm,xpk",
311#else
312 .p.extensions = "669,amf,ams,dbm,digi,dmf,dsm,far,gdm,ice,imf,it,j2b,m15,mdl,med,mmcmp,mms,mo3,mod,mptm,mt2,mtm,nst,okt,plm,ppm,psm,pt36,ptm,s3m,sfx,sfx2,st26,stk,stm,ult,umx,wow,xm,xpk",
313#endif
314 .priv_data_size = sizeof(OpenMPTContext),
315 .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP,
321};
const FFInputFormat ff_libopenmpt_demuxer
Definition libopenmpt.c:305
#define A(x)
Definition vpx_arith.h:28
#define D
Definition avdct.c:35
void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition avformat.c:865
Main libavformat public API header.
#define AVPROBE_SCORE_MAX
maximum score
Definition avformat.h:485
#define AVPROBE_SCORE_EXTENSION
score for file extension
Definition avformat.h:483
#define AVPROBE_SCORE_RETRY
Definition avformat.h:480
int64_t avio_size(AVIOContext *s)
Get the filesize.
Definition aviobuf.c:326
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition aviobuf.c:620
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define s(width, name)
Definition cbs_vp9.c:198
static int read_probe(const AVProbeData *p)
Definition cdg.c:30
Public libavutil channel layout APIs header.
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
#define FF_INFMT_FLAG_INIT_CLEANUP
For an FFInputFormat with this flag set read_close() needs to be called by the caller upon read_heade...
Definition demux.h:35
static AVPacket * pkt
int ret
Definition ffplay.c:391
static int read_header(FFV1Context *f, RangeCoder *c)
Definition ffv1dec.c:580
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_CHLAYOUT
Underlying C type is AVChannelLayout.
Definition opt.h:330
@ AV_CODEC_ID_PCM_F32LE
Definition codec_id.h:354
@ AV_CODEC_ID_PCM_F32BE
Definition codec_id.h:353
int av_new_packet(AVPacket *pkt, int size)
Allocate the payload of a packet and initialize its fields with default values.
Definition packet.c:98
AVStream * avformat_new_stream(AVFormatContext *s, const struct AVCodec *c)
Add a new stream to a media file.
int av_channel_layout_copy(AVChannelLayout *dst, const AVChannelLayout *src)
Make a copy of a channel layout.
int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value, int flags)
Convenience wrapper for av_dict_set() that converts the value to a string and stores it.
Definition dict.c:180
#define AVERROR_UNKNOWN
Unknown error, typically from an external library.
Definition error.h:73
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR_EOF
End of file.
Definition error.h:57
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_INFO
Standard information.
Definition log.h:221
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:247
@ AVMEDIA_TYPE_AUDIO
Definition avutil.h:201
#define AV_TIME_BASE
Internal time base represented as integer.
Definition avutil.h:253
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:97
static av_cold int read_close(AVFormatContext *ctx)
Definition libcdio.c:143
static int read_seek(AVFormatContext *ctx, int stream_index, int64_t timestamp, int flags)
Definition libcdio.c:151
#define AUDIO_PKT_SIZE
Definition libgme.c:82
static int read_close_openmpt(AVFormatContext *s)
Definition libopenmpt.c:223
static void openmpt_logfunc(const char *message, void *userdata)
Definition libopenmpt.c:70
#define add_meta(s, name, meta)
Definition libopenmpt.c:79
static int read_header_openmpt(AVFormatContext *s)
Definition libopenmpt.c:87
static int probe_openmpt_extension(const AVProbeData *p)
Definition libopenmpt.c:242
static const AVClass class_openmpt
Definition libopenmpt.c:298
static int read_seek_openmpt(AVFormatContext *s, int stream_idx, int64_t ts, int flags)
Definition libopenmpt.c:233
static int read_probe_openmpt(const AVProbeData *p)
Definition libopenmpt.c:256
#define OFFSET(x)
Definition libopenmpt.c:52
static int read_packet_openmpt(AVFormatContext *s, AVPacket *pkt)
Definition libopenmpt.c:181
#define AV_NE(be, le)
Definition macros.h:33
Memory handling functions.
#define av_malloc(s)
Definition ops_static.c:52
AVOptions.
unsigned int pos
Definition spdifenc.c:431
An AVChannelLayout holds information about the channel layout of audio data.
int nb_channels
Number of channels in this layout.
Describe the class of an AVClass context structure.
Definition log.h:76
AVChannelLayout ch_layout
The channel layout and number of channels.
Definition codec_par.h:207
enum AVMediaType codec_type
General type of the encoded data.
Definition codec_par.h:53
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition codec_par.h:57
int sample_rate
The number of audio samples per second.
Definition codec_par.h:213
Format I/O context.
Definition avformat.h:1401
AVOption.
Definition opt.h:428
This structure stores compressed data.
Definition packet.h:587
This structure contains the data a format has to probe a file.
Definition avformat.h:473
Stream structure.
Definition avformat.h:768
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition avformat.h:791
int64_t duration
Decoding: duration of the stream, in stream time base.
Definition avformat.h:827
openmpt_module * module
Definition libopenmpt.c:42
double duration
Definition libopenmpt.c:44
AVChannelLayout ch_layout
Definition libopenmpt.c:47
uint8_t level
Definition svq3.c:208
#define llrint
Definition tablegen.h:47
#define av_freep(p)
#define av_log(a,...)
static void error(const char *err)
int size
interp
Definition vf_curves.c:62