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/channel_layout.h"
34 #include "libavutil/mem.h"
35 #include "libavutil/opt.h"
40 typedef struct OpenMPTContext
{
42 openmpt_module
*module
;
47 AVChannelLayout ch_layout
;
51 #define OFFSET(x) offsetof(OpenMPTContext, x)
52 #define A AV_OPT_FLAG_AUDIO_PARAM
53 #define D AV_OPT_FLAG_DECODING_PARAM
54 static const AVOption options
[] = {
55 { "sample_rate", "set sample rate", OFFSET(sample_rate
), AV_OPT_TYPE_INT
, { .i64
= 48000 }, 1000, INT_MAX
, A
| D
},
56 { "layout", "set channel layout", OFFSET(ch_layout
), AV_OPT_TYPE_CHLAYOUT
, { .str
= "stereo" }, 0, 0, A
| D
},
57 { "subsong", "set subsong", OFFSET(subsong
), AV_OPT_TYPE_INT
, { .i64
= -2 }, -2, INT_MAX
, A
| D
, .unit
= "subsong"},
58 { "all", "all", 0, AV_OPT_TYPE_CONST
, { .i64
= -1}, 0, 0, A
| D
, .unit
= "subsong" },
59 { "auto", "auto", 0, AV_OPT_TYPE_CONST
, { .i64
= -2}, 0, 0, A
| D
, .unit
= "subsong" },
63 static void openmpt_logfunc(const char *message
, void *userdata
)
65 int level
= AV_LOG_INFO
;
66 if (strstr(message
, "ERROR") != NULL
) {
69 av_log(userdata
, level
, "%s\n", message
);
72 #define add_meta(s, name, meta) \
74 const char *value = meta; \
75 if (value && value[0]) \
76 av_dict_set(&s->metadata, name, value, 0); \
77 openmpt_free_string(value); \
80 static int read_header_openmpt(AVFormatContext
*s
)
83 OpenMPTContext
*openmpt
= s
->priv_data
;
86 #if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
91 size
= avio_size(s
->pb
);
93 return AVERROR_INVALIDDATA
;
94 buf
= av_malloc(size
);
96 return AVERROR(ENOMEM
);
97 size
= avio_read(s
->pb
, buf
, size
);
99 av_log(s
, AV_LOG_ERROR
, "Reading input buffer failed.\n");
104 #if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
105 error
= OPENMPT_ERROR_OK
;
106 openmpt
->module
= openmpt_module_create_from_memory2(buf
, size
, openmpt_logfunc
, s
, NULL
, NULL
, &error
, NULL
, NULL
);
108 if (!openmpt
->module
) {
109 if (error
== OPENMPT_ERROR_OUT_OF_MEMORY
)
110 return AVERROR(ENOMEM
);
111 else if (error
>= OPENMPT_ERROR_GENERAL
)
112 return AVERROR_INVALIDDATA
;
114 return AVERROR_UNKNOWN
;
117 openmpt
->module
= openmpt_module_create_from_memory(buf
, size
, openmpt_logfunc
, s
, NULL
);
119 if (!openmpt
->module
)
120 return AVERROR_INVALIDDATA
;
123 if (openmpt
->subsong
>= openmpt_module_get_num_subsongs(openmpt
->module
)) {
124 av_log(s
, AV_LOG_ERROR
, "Invalid subsong index: %d\n", openmpt
->subsong
);
125 return AVERROR(EINVAL
);
128 if (openmpt
->subsong
!= -2) {
129 if (openmpt
->subsong
>= 0) {
130 av_dict_set_int(&s
->metadata
, "track", openmpt
->subsong
+ 1, 0);
132 ret
= openmpt_module_select_subsong(openmpt
->module
, openmpt
->subsong
);
134 av_log(s
, AV_LOG_ERROR
, "Could not select requested subsong: %d", openmpt
->subsong
);
135 return AVERROR(EINVAL
);
139 openmpt
->duration
= openmpt_module_get_duration_seconds(openmpt
->module
);
141 add_meta(s
, "artist", openmpt_module_get_metadata(openmpt
->module
, "artist"));
142 add_meta(s
, "title", openmpt_module_get_metadata(openmpt
->module
, "title"));
143 add_meta(s
, "encoder", openmpt_module_get_metadata(openmpt
->module
, "tracker"));
144 add_meta(s
, "comment", openmpt_module_get_metadata(openmpt
->module
, "message"));
145 add_meta(s
, "date", openmpt_module_get_metadata(openmpt
->module
, "date"));
147 st
= avformat_new_stream(s
, NULL
);
149 return AVERROR(ENOMEM
);
150 avpriv_set_pts_info(st
, 64, 1, AV_TIME_BASE
);
151 if (openmpt
->duration
>= 0 && openmpt
->duration
< ((double)INT64_MAX
+ 1) / AV_TIME_BASE
)
152 st
->duration
= llrint(openmpt
->duration
*AV_TIME_BASE
);
154 st
->codecpar
->codec_type
= AVMEDIA_TYPE_AUDIO
;
155 st
->codecpar
->codec_id
= AV_NE(AV_CODEC_ID_PCM_F32BE
, AV_CODEC_ID_PCM_F32LE
);
156 st
->codecpar
->sample_rate
= openmpt
->sample_rate
;
157 ret
= av_channel_layout_copy(&st
->codecpar
->ch_layout
, &openmpt
->ch_layout
);
164 #define AUDIO_PKT_SIZE 2048
166 static int read_packet_openmpt(AVFormatContext
*s
, AVPacket
*pkt
)
168 OpenMPTContext
*openmpt
= s
->priv_data
;
169 int n_samples
= AUDIO_PKT_SIZE
/ (openmpt
->ch_layout
.nb_channels
? openmpt
->ch_layout
.nb_channels
*4 : 4);
172 if ((ret
= av_new_packet(pkt
, AUDIO_PKT_SIZE
)) < 0)
175 double pos
= openmpt_module_get_position_seconds(openmpt
->module
);
177 switch (openmpt
->ch_layout
.nb_channels
) {
179 ret
= openmpt_module_read_float_mono(openmpt
->module
, openmpt
->sample_rate
,
180 n_samples
, (float *)pkt
->data
);
183 ret
= openmpt_module_read_interleaved_float_stereo(openmpt
->module
, openmpt
->sample_rate
,
184 n_samples
, (float *)pkt
->data
);
187 ret
= openmpt_module_read_interleaved_float_quad(openmpt
->module
, openmpt
->sample_rate
,
188 n_samples
, (float *)pkt
->data
);
191 av_log(s
, AV_LOG_ERROR
, "Unsupported number of channels: %d", openmpt
->ch_layout
.nb_channels
);
192 return AVERROR(EINVAL
);
200 pkt
->size
= ret
* (openmpt
->ch_layout
.nb_channels
* 4);
202 if (pos
>= 0 && pos
< ((double)INT64_MAX
+ 1) / AV_TIME_BASE
)
203 pkt
->pts
= llrint(pos
* AV_TIME_BASE
);
208 static int read_close_openmpt(AVFormatContext
*s
)
210 OpenMPTContext
*openmpt
= s
->priv_data
;
211 if (openmpt
->module
) {
212 openmpt_module_destroy(openmpt
->module
);
213 openmpt
->module
= NULL
;
218 static int read_seek_openmpt(AVFormatContext
*s
, int stream_idx
, int64_t ts
, int flags
)
220 OpenMPTContext
*openmpt
= s
->priv_data
;
223 openmpt_module_set_position_seconds(openmpt
->module
, (double)ts
/AV_TIME_BASE
);
227 static int probe_openmpt_extension(const AVProbeData
*p
)
231 ext
= strrchr(p
->filename
, '.');
232 if (ext
&& strlen(ext
+ 1) > 0) {
233 ext
++; /* skip '.' */
234 if (openmpt_is_extension_supported(ext
) == 1)
235 return AVPROBE_SCORE_EXTENSION
;
241 static int read_probe_openmpt(const AVProbeData
*p
)
243 #if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
245 if (p
->buf
&& p
->buf_size
> 0) {
246 probe_result
= openmpt_probe_file_header_without_filesize(
247 OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT
,
249 &openmpt_logfunc
, NULL
, NULL
, NULL
, NULL
, NULL
);
250 if (probe_result
== OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS
) {
251 /* As probing here relies on code external to FFmpeg, do not return
252 * AVPROBE_SCORE_MAX in order to reduce the impact in the rare
253 * cases of false positives.
255 return (AVPROBE_SCORE_MAX
* 3) / 4 + 1;
256 } else if (probe_result
== OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA
) {
257 if (probe_openmpt_extension(p
) > 0) {
258 return AVPROBE_SCORE_RETRY
;
260 if (p
->buf_size
>= openmpt_probe_file_header_get_recommended_size()) {
261 /* We have already received the recommended amount of data
262 * and still cannot decide. Return a rather low score.
264 return AVPROBE_SCORE_RETRY
/ 2;
266 /* The file extension is unknown and we have very few data
267 * bytes available. libopenmpt cannot decide anything here,
268 * and returning any score > 0 would result in successful
269 * probing of random data.
274 } else if (probe_result
== OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE
) {
279 /* for older libopenmpt, fall back to file extension probing */
280 return probe_openmpt_extension(p
);
283 static const AVClass class_openmpt
= {
284 .class_name
= "libopenmpt",
285 .item_name
= av_default_item_name
,
287 .version
= LIBAVUTIL_VERSION_INT
,
290 const FFInputFormat ff_libopenmpt_demuxer
= {
291 .p
.name
= "libopenmpt",
292 .p
.long_name
= NULL_IF_CONFIG_SMALL("Tracker formats (libopenmpt)"),
293 .p
.priv_class
= &class_openmpt
,
294 #if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
295 .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",
297 .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",
299 .priv_data_size
= sizeof(OpenMPTContext
),
300 .flags_internal
= FF_INFMT_FLAG_INIT_CLEANUP
,
301 .read_probe
= read_probe_openmpt
,
302 .read_header
= read_header_openmpt
,
303 .read_packet
= read_packet_openmpt
,
304 .read_close
= read_close_openmpt
,
305 .read_seek
= read_seek_openmpt
,