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/avstring.h"
26 #include "libavutil/eval.h"
27 #include "libavutil/opt.h"
31 typedef struct GMEContext
{
34 gme_info_t
*info
; ///< selected track
42 #define OFFSET(x) offsetof(GMEContext, x)
43 #define A AV_OPT_FLAG_AUDIO_PARAM
44 #define D AV_OPT_FLAG_DECODING_PARAM
45 static const AVOption options
[] = {
46 {"track_index", "set track that should be played", OFFSET(track_index
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, INT_MAX
, A
|D
},
47 {"sample_rate", "set sample rate", OFFSET(sample_rate
), AV_OPT_TYPE_INT
, {.i64
= 44100}, 1000, 999999, A
|D
},
48 {"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
},
52 static void add_meta(AVFormatContext
*s
, const char *name
, const char *value
)
54 if (value
&& value
[0])
55 av_dict_set(&s
->metadata
, name
, value
, 0);
58 static int load_metadata(AVFormatContext
*s
)
60 GMEContext
*gme
= s
->priv_data
;
61 gme_info_t
*info
= gme
->info
;
64 add_meta(s
, "system", info
->system
);
65 add_meta(s
, "game", info
->game
);
66 add_meta(s
, "song", info
->song
);
67 add_meta(s
, "author", info
->author
);
68 add_meta(s
, "copyright", info
->copyright
);
69 add_meta(s
, "comment", info
->comment
);
70 add_meta(s
, "dumper", info
->dumper
);
72 snprintf(buf
, sizeof(buf
), "%d", (int)gme_track_count(gme
->music_emu
));
73 add_meta(s
, "tracks", buf
);
78 #define AUDIO_PKT_SIZE 512
80 static int read_header_gme(AVFormatContext
*s
)
83 AVIOContext
*pb
= s
->pb
;
84 GMEContext
*gme
= s
->priv_data
;
85 int64_t sz
= avio_size(pb
);
90 av_log(s
, AV_LOG_WARNING
, "Could not determine file size\n");
92 } else if (gme
->max_size
&& sz
> gme
->max_size
) {
98 return AVERROR(ENOMEM
);
99 sz
= avio_read(pb
, buf
, sz
);
101 // Data left means our buffer (the max_size option) is too small
102 if (avio_read(pb
, &dummy
, 1) == 1) {
103 av_log(s
, AV_LOG_ERROR
, "File size is larger than max_size option "
104 "value %"PRIi64
", consider increasing the max_size option\n",
106 return AVERROR_BUFFER_TOO_SMALL
;
109 if (gme_open_data(buf
, sz
, &gme
->music_emu
, gme
->sample_rate
)) {
111 return AVERROR_INVALIDDATA
;
115 if (gme_track_info(gme
->music_emu
, &gme
->info
, gme
->track_index
))
116 return AVERROR_STREAM_NOT_FOUND
;
118 if (gme_start_track(gme
->music_emu
, gme
->track_index
))
119 return AVERROR_UNKNOWN
;
123 st
= avformat_new_stream(s
, NULL
);
125 return AVERROR(ENOMEM
);
126 avpriv_set_pts_info(st
, 64, 1, 1000);
127 if (st
->duration
> 0)
128 st
->duration
= gme
->info
->length
;
129 st
->codec
->codec_type
= AVMEDIA_TYPE_AUDIO
;
130 st
->codec
->codec_id
= AV_NE(AV_CODEC_ID_PCM_S16BE
, AV_CODEC_ID_PCM_S16LE
);
131 st
->codec
->channels
= 2;
132 st
->codec
->sample_rate
= gme
->sample_rate
;
137 static int read_packet_gme(AVFormatContext
*s
, AVPacket
*pkt
)
139 GMEContext
*gme
= s
->priv_data
;
140 int n_samples
= AUDIO_PKT_SIZE
/ 2;
143 if (gme_track_ended(gme
->music_emu
))
146 if ((ret
= av_new_packet(pkt
, AUDIO_PKT_SIZE
)) < 0)
149 if (gme_play(gme
->music_emu
, n_samples
, (short *)pkt
->data
))
150 return AVERROR_EXTERNAL
;
151 pkt
->size
= AUDIO_PKT_SIZE
;
156 static int read_close_gme(AVFormatContext
*s
)
158 GMEContext
*gme
= s
->priv_data
;
159 gme_free_info(gme
->info
);
160 gme_delete(gme
->music_emu
);
164 static int read_seek_gme(AVFormatContext
*s
, int stream_idx
, int64_t ts
, int flags
)
166 GMEContext
*gme
= s
->priv_data
;
167 if (!gme_seek(gme
->music_emu
, (int)ts
))
168 return AVERROR_EXTERNAL
;
172 static int probe_gme(AVProbeData
*p
)
174 // Reads 4 bytes - returns "" if unknown format.
175 if (gme_identify_header(p
->buf
)[0]) {
176 if (p
->buf_size
< 16384)
177 return AVPROBE_SCORE_MAX
/ 4 ;
179 return AVPROBE_SCORE_MAX
/ 2;
184 static const AVClass class_gme
= {
185 .class_name
= "Game Music Emu demuxer",
186 .item_name
= av_default_item_name
,
188 .version
= LIBAVUTIL_VERSION_INT
,
191 AVInputFormat ff_libgme_demuxer
= {
193 .long_name
= NULL_IF_CONFIG_SMALL("Game Music Emu demuxer"),
194 .priv_data_size
= sizeof(GMEContext
),
195 .read_probe
= probe_gme
,
196 .read_header
= read_header_gme
,
197 .read_packet
= read_packet_gme
,
198 .read_close
= read_close_gme
,
199 .read_seek
= read_seek_gme
,
200 .priv_class
= &class_gme
,