2 * This file is part of FFmpeg.
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.
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.
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
25 #include "libavutil/mem.h"
26 #include "libavutil/opt.h"
31 typedef struct GMEContext
{
41 #define OFFSET(x) offsetof(GMEContext, x)
42 #define A AV_OPT_FLAG_AUDIO_PARAM
43 #define D AV_OPT_FLAG_DECODING_PARAM
44 static const AVOption options
[] = {
45 {"track_index", "set track that should be played", OFFSET(track_index
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, A
|D
},
46 {"sample_rate", "set sample rate", OFFSET(sample_rate
), AV_OPT_TYPE_INT
, {.i64
= 44100}, 1000, 999999, A
|D
},
47 {"max_size", "set max file size supported (in bytes)", OFFSET(max_size
), AV_OPT_TYPE_INT64
, {.i64
= 50 * 1024 * 1024}, 0, SIZE_MAX
, A
|D
},
51 static void add_meta(AVFormatContext
*s
, const char *name
, const char *value
)
53 if (value
&& value
[0])
54 av_dict_set(&s
->metadata
, name
, value
, 0);
57 static int load_metadata(AVFormatContext
*s
, int64_t *duration
)
59 GMEContext
*gme
= s
->priv_data
;
60 gme_info_t
*info
= NULL
;
63 if (gme_track_info(gme
->music_emu
, &info
, gme
->track_index
))
64 return AVERROR_STREAM_NOT_FOUND
;
66 *duration
= info
->length
;
67 add_meta(s
, "system", info
->system
);
68 add_meta(s
, "game", info
->game
);
69 add_meta(s
, "song", info
->song
);
70 add_meta(s
, "author", info
->author
);
71 add_meta(s
, "copyright", info
->copyright
);
72 add_meta(s
, "comment", info
->comment
);
73 add_meta(s
, "dumper", info
->dumper
);
75 snprintf(buf
, sizeof(buf
), "%d", (int)gme_track_count(gme
->music_emu
));
76 add_meta(s
, "tracks", buf
);
82 #define AUDIO_PKT_SIZE 512
84 static int read_close_gme(AVFormatContext
*s
)
86 GMEContext
*gme
= s
->priv_data
;
88 gme_delete(gme
->music_emu
);
92 static int read_header_gme(AVFormatContext
*s
)
95 AVIOContext
*pb
= s
->pb
;
96 GMEContext
*gme
= s
->priv_data
;
97 int64_t sz
= avio_size(pb
);
104 av_log(s
, AV_LOG_WARNING
, "Could not determine file size\n");
106 } else if (gme
->max_size
&& sz
> gme
->max_size
) {
112 return AVERROR(ENOMEM
);
113 sz
= avio_read(pb
, buf
, sz
);
115 // Data left means our buffer (the max_size option) is too small
116 if (avio_read(pb
, &dummy
, 1) == 1) {
117 av_log(s
, AV_LOG_ERROR
, "File size is larger than max_size option "
118 "value %"PRIi64
", consider increasing the max_size option\n",
121 return AVERROR_BUFFER_TOO_SMALL
;
124 if (gme_open_data(buf
, sz
, &gme
->music_emu
, gme
->sample_rate
)) {
125 gme
->music_emu
= NULL
; /* Just for safety */
127 return AVERROR_INVALIDDATA
;
131 ret
= load_metadata(s
, &duration
);
134 if (gme_start_track(gme
->music_emu
, gme
->track_index
))
135 return AVERROR_UNKNOWN
;
137 st
= avformat_new_stream(s
, NULL
);
139 return AVERROR(ENOMEM
);
140 avpriv_set_pts_info(st
, 64, 1, 1000);
142 st
->duration
= duration
;
143 st
->codecpar
->codec_type
= AVMEDIA_TYPE_AUDIO
;
144 st
->codecpar
->codec_id
= AV_NE(AV_CODEC_ID_PCM_S16BE
, AV_CODEC_ID_PCM_S16LE
);
145 st
->codecpar
->ch_layout
.nb_channels
= 2;
146 st
->codecpar
->sample_rate
= gme
->sample_rate
;
151 static int read_packet_gme(AVFormatContext
*s
, AVPacket
*pkt
)
153 GMEContext
*gme
= s
->priv_data
;
154 int n_samples
= AUDIO_PKT_SIZE
/ 2;
157 if (gme_track_ended(gme
->music_emu
))
160 if ((ret
= av_new_packet(pkt
, AUDIO_PKT_SIZE
)) < 0)
163 if (gme_play(gme
->music_emu
, n_samples
, (short *)pkt
->data
))
164 return AVERROR_EXTERNAL
;
169 static int read_seek_gme(AVFormatContext
*s
, int stream_idx
, int64_t ts
, int flags
)
171 GMEContext
*gme
= s
->priv_data
;
172 if (!gme_seek(gme
->music_emu
, (int)ts
))
173 return AVERROR_EXTERNAL
;
177 static int probe_gme(const AVProbeData
*p
)
179 // Reads 4 bytes - returns "" if unknown format.
180 if (gme_identify_header(p
->buf
)[0]) {
181 if (p
->buf_size
< 16384)
182 return AVPROBE_SCORE_MAX
/ 4 ;
184 return AVPROBE_SCORE_MAX
/ 2;
189 static const AVClass class_gme
= {
190 .class_name
= "Game Music Emu demuxer",
191 .item_name
= av_default_item_name
,
193 .version
= LIBAVUTIL_VERSION_INT
,
196 const FFInputFormat ff_libgme_demuxer
= {
198 .p
.long_name
= NULL_IF_CONFIG_SMALL("Game Music Emu demuxer"),
199 .p
.priv_class
= &class_gme
,
200 .priv_data_size
= sizeof(GMEContext
),
201 .flags_internal
= FF_INFMT_FLAG_INIT_CLEANUP
,
202 .read_probe
= probe_gme
,
203 .read_header
= read_header_gme
,
204 .read_packet
= read_packet_gme
,
205 .read_close
= read_close_gme
,
206 .read_seek
= read_seek_gme
,