2 * Tracked MOD demuxer (libopenmpt)
3 * Copyright (c) 2016 Josh de Kock
5 * This file is part of FFmpeg.
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.
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.
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
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))
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)))
33 #include "libavutil/avstring.h"
34 #include "libavutil/opt.h"
38 typedef struct OpenMPTContext
{
40 openmpt_module
*module
;
50 #define OFFSET(x) offsetof(OpenMPTContext, x)
51 #define A AV_OPT_FLAG_AUDIO_PARAM
52 #define D AV_OPT_FLAG_DECODING_PARAM
53 static const AVOption options
[] = {
54 { "sample_rate", "set sample rate", OFFSET(sample_rate
), AV_OPT_TYPE_INT
, { .i64
= 48000 }, 1000, INT_MAX
, A
| D
},
55 { "layout", "set channel layout", OFFSET(layout
), AV_OPT_TYPE_CHANNEL_LAYOUT
, { .i64
= AV_CH_LAYOUT_STEREO
}, 0, INT64_MAX
, A
| D
},
56 { "subsong", "set subsong", OFFSET(subsong
), AV_OPT_TYPE_INT
, { .i64
= -2 }, -2, INT_MAX
, A
| D
, "subsong"},
57 { "all", "all", 0, AV_OPT_TYPE_CONST
, { .i64
= -1}, 0, 0, A
| D
, "subsong" },
58 { "auto", "auto", 0, AV_OPT_TYPE_CONST
, { .i64
= -2}, 0, 0, A
| D
, "subsong" },
62 static void openmpt_logfunc(const char *message
, void *userdata
)
64 int level
= AV_LOG_INFO
;
65 if (strstr(message
, "ERROR") != NULL
) {
68 av_log(userdata
, level
, "%s\n", message
);
71 #define add_meta(s, name, meta) \
73 const char *value = meta; \
74 if (value && value[0]) \
75 av_dict_set(&s->metadata, name, value, 0); \
76 openmpt_free_string(value); \
79 static int read_header_openmpt(AVFormatContext
*s
)
82 OpenMPTContext
*openmpt
= s
->priv_data
;
85 #if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
90 size
= avio_size(s
->pb
);
92 return AVERROR_INVALIDDATA
;
93 buf
= av_malloc(size
);
95 return AVERROR(ENOMEM
);
96 size
= avio_read(s
->pb
, buf
, size
);
98 av_log(s
, AV_LOG_ERROR
, "Reading input buffer failed.\n");
103 #if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
104 error
= OPENMPT_ERROR_OK
;
105 openmpt
->module
= openmpt_module_create_from_memory2(buf
, size
, openmpt_logfunc
, s
, NULL
, NULL
, &error
, NULL
, NULL
);
107 if (!openmpt
->module
) {
108 if (error
== OPENMPT_ERROR_OUT_OF_MEMORY
)
109 return AVERROR(ENOMEM
);
110 else if (error
>= OPENMPT_ERROR_GENERAL
)
111 return AVERROR_INVALIDDATA
;
113 return AVERROR_UNKNOWN
;
116 openmpt
->module
= openmpt_module_create_from_memory(buf
, size
, openmpt_logfunc
, s
, NULL
);
118 if (!openmpt
->module
)
119 return AVERROR_INVALIDDATA
;
122 openmpt
->channels
= av_get_channel_layout_nb_channels(openmpt
->layout
);
124 if (openmpt
->subsong
>= openmpt_module_get_num_subsongs(openmpt
->module
)) {
125 openmpt_module_destroy(openmpt
->module
);
126 av_log(s
, AV_LOG_ERROR
, "Invalid subsong index: %d\n", openmpt
->subsong
);
127 return AVERROR(EINVAL
);
130 if (openmpt
->subsong
!= -2) {
131 if (openmpt
->subsong
>= 0) {
132 av_dict_set_int(&s
->metadata
, "track", openmpt
->subsong
+ 1, 0);
134 ret
= openmpt_module_select_subsong(openmpt
->module
, openmpt
->subsong
);
136 openmpt_module_destroy(openmpt
->module
);
137 av_log(s
, AV_LOG_ERROR
, "Could not select requested subsong: %d", openmpt
->subsong
);
138 return AVERROR(EINVAL
);
142 openmpt
->duration
= openmpt_module_get_duration_seconds(openmpt
->module
);
144 add_meta(s
, "artist", openmpt_module_get_metadata(openmpt
->module
, "artist"));
145 add_meta(s
, "title", openmpt_module_get_metadata(openmpt
->module
, "title"));
146 add_meta(s
, "encoder", openmpt_module_get_metadata(openmpt
->module
, "tracker"));
147 add_meta(s
, "comment", openmpt_module_get_metadata(openmpt
->module
, "message"));
148 add_meta(s
, "date", openmpt_module_get_metadata(openmpt
->module
, "date"));
150 st
= avformat_new_stream(s
, NULL
);
152 openmpt_module_destroy(openmpt
->module
);
153 openmpt
->module
= NULL
;
154 return AVERROR(ENOMEM
);
156 avpriv_set_pts_info(st
, 64, 1, AV_TIME_BASE
);
157 st
->duration
= llrint(openmpt
->duration
*AV_TIME_BASE
);
159 st
->codecpar
->codec_type
= AVMEDIA_TYPE_AUDIO
;
160 st
->codecpar
->codec_id
= AV_NE(AV_CODEC_ID_PCM_F32BE
, AV_CODEC_ID_PCM_F32LE
);
161 st
->codecpar
->channels
= openmpt
->channels
;
162 st
->codecpar
->sample_rate
= openmpt
->sample_rate
;
167 #define AUDIO_PKT_SIZE 2048
169 static int read_packet_openmpt(AVFormatContext
*s
, AVPacket
*pkt
)
171 OpenMPTContext
*openmpt
= s
->priv_data
;
172 int n_samples
= AUDIO_PKT_SIZE
/ (openmpt
->channels
? openmpt
->channels
*4 : 4);
175 if ((ret
= av_new_packet(pkt
, AUDIO_PKT_SIZE
)) < 0)
178 switch (openmpt
->channels
) {
180 ret
= openmpt_module_read_float_mono(openmpt
->module
, openmpt
->sample_rate
,
181 n_samples
, (float *)pkt
->data
);
184 ret
= openmpt_module_read_interleaved_float_stereo(openmpt
->module
, openmpt
->sample_rate
,
185 n_samples
, (float *)pkt
->data
);
188 ret
= openmpt_module_read_interleaved_float_quad(openmpt
->module
, openmpt
->sample_rate
,
189 n_samples
, (float *)pkt
->data
);
192 av_log(s
, AV_LOG_ERROR
, "Unsupported number of channels: %d", openmpt
->channels
);
193 return AVERROR(EINVAL
);
201 pkt
->size
= ret
* (openmpt
->channels
* 4);
206 static int read_close_openmpt(AVFormatContext
*s
)
208 OpenMPTContext
*openmpt
= s
->priv_data
;
209 openmpt_module_destroy(openmpt
->module
);
210 openmpt
->module
= NULL
;
214 static int read_seek_openmpt(AVFormatContext
*s
, int stream_idx
, int64_t ts
, int flags
)
216 OpenMPTContext
*openmpt
= s
->priv_data
;
217 openmpt_module_set_position_seconds(openmpt
->module
, (double)ts
/AV_TIME_BASE
);
221 static int probe_openmpt_extension(AVProbeData
*p
)
225 ext
= strrchr(p
->filename
, '.');
226 if (ext
&& strlen(ext
+ 1) > 0) {
227 ext
++; /* skip '.' */
228 if (openmpt_is_extension_supported(ext
) == 1)
229 return AVPROBE_SCORE_EXTENSION
;
235 static int read_probe_openmpt(const AVProbeData
*p
)
237 #if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
239 if (p
->buf
&& p
->buf_size
> 0) {
240 probe_result
= openmpt_probe_file_header_without_filesize(
241 OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT
,
243 &openmpt_logfunc
, NULL
, NULL
, NULL
, NULL
, NULL
);
244 if (probe_result
== OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS
) {
245 /* As probing here relies on code external to FFmpeg, do not return
246 * AVPROBE_SCORE_MAX in order to reduce the impact in the rare
247 * cases of false positives.
249 return AVPROBE_SCORE_MIME
+ 1;
250 } else if (probe_result
== OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA
) {
251 if (probe_openmpt_extension(p
) > 0) {
252 return AVPROBE_SCORE_RETRY
;
254 if (p
->buf_size
>= openmpt_probe_file_header_get_recommended_size()) {
255 /* We have already received the recommended amount of data
256 * and still cannot decide. Return a rather low score.
258 return AVPROBE_SCORE_RETRY
/ 2;
260 /* The file extension is unknown and we have very few data
261 * bytes available. libopenmpt cannot decide anything here,
262 * and returning any score > 0 would result in successful
263 * probing of random data.
268 } else if (probe_result
== OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE
) {
273 /* for older libopenmpt, fall back to file extension probing */
274 return probe_openmpt_extension(p
);
277 static const AVClass class_openmpt
= {
278 .class_name
= "libopenmpt",
279 .item_name
= av_default_item_name
,
281 .version
= LIBAVUTIL_VERSION_INT
,
284 AVInputFormat ff_libopenmpt_demuxer
= {
285 .name
= "libopenmpt",
286 .long_name
= NULL_IF_CONFIG_SMALL("Tracker formats (libopenmpt)"),
287 .priv_data_size
= sizeof(OpenMPTContext
),
288 .read_probe
= read_probe_openmpt
,
289 .read_header
= read_header_openmpt
,
290 .read_packet
= read_packet_openmpt
,
291 .read_close
= read_close_openmpt
,
292 .read_seek
= read_seek_openmpt
,
293 .priv_class
= &class_openmpt
,
294 #if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
295 .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",
297 .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",