xref: /third_party/ffmpeg/libavformat/libgme.c (revision cabdff1a)
1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * This file is part of FFmpeg.
3cabdff1aSopenharmony_ci *
4cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
5cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
6cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
7cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
8cabdff1aSopenharmony_ci *
9cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
10cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
11cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12cabdff1aSopenharmony_ci * Lesser General Public License for more details.
13cabdff1aSopenharmony_ci *
14cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
15cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
16cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17cabdff1aSopenharmony_ci */
18cabdff1aSopenharmony_ci
19cabdff1aSopenharmony_ci/**
20cabdff1aSopenharmony_ci* @file
21cabdff1aSopenharmony_ci* libgme demuxer
22cabdff1aSopenharmony_ci*/
23cabdff1aSopenharmony_ci
24cabdff1aSopenharmony_ci#include <gme/gme.h>
25cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
26cabdff1aSopenharmony_ci#include "libavutil/eval.h"
27cabdff1aSopenharmony_ci#include "libavutil/opt.h"
28cabdff1aSopenharmony_ci#include "avformat.h"
29cabdff1aSopenharmony_ci#include "internal.h"
30cabdff1aSopenharmony_ci
31cabdff1aSopenharmony_citypedef struct GMEContext {
32cabdff1aSopenharmony_ci    const AVClass *class;
33cabdff1aSopenharmony_ci    Music_Emu *music_emu;
34cabdff1aSopenharmony_ci
35cabdff1aSopenharmony_ci    /* options */
36cabdff1aSopenharmony_ci    int track_index;
37cabdff1aSopenharmony_ci    int sample_rate;
38cabdff1aSopenharmony_ci    int64_t max_size;
39cabdff1aSopenharmony_ci} GMEContext;
40cabdff1aSopenharmony_ci
41cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(GMEContext, x)
42cabdff1aSopenharmony_ci#define A AV_OPT_FLAG_AUDIO_PARAM
43cabdff1aSopenharmony_ci#define D AV_OPT_FLAG_DECODING_PARAM
44cabdff1aSopenharmony_cistatic const AVOption options[] = {
45cabdff1aSopenharmony_ci    {"track_index", "set track that should be played",        OFFSET(track_index), AV_OPT_TYPE_INT,   {.i64 = 0},                0,    INT_MAX,  A|D},
46cabdff1aSopenharmony_ci    {"sample_rate", "set sample rate",                        OFFSET(sample_rate), AV_OPT_TYPE_INT,   {.i64 = 44100},            1000, 999999,   A|D},
47cabdff1aSopenharmony_ci    {"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},
48cabdff1aSopenharmony_ci    {NULL}
49cabdff1aSopenharmony_ci};
50cabdff1aSopenharmony_ci
51cabdff1aSopenharmony_cistatic void add_meta(AVFormatContext *s, const char *name, const char *value)
52cabdff1aSopenharmony_ci{
53cabdff1aSopenharmony_ci    if (value && value[0])
54cabdff1aSopenharmony_ci        av_dict_set(&s->metadata, name, value, 0);
55cabdff1aSopenharmony_ci}
56cabdff1aSopenharmony_ci
57cabdff1aSopenharmony_cistatic int load_metadata(AVFormatContext *s, int64_t *duration)
58cabdff1aSopenharmony_ci{
59cabdff1aSopenharmony_ci    GMEContext *gme = s->priv_data;
60cabdff1aSopenharmony_ci    gme_info_t *info  = NULL;
61cabdff1aSopenharmony_ci    char buf[30];
62cabdff1aSopenharmony_ci
63cabdff1aSopenharmony_ci    if (gme_track_info(gme->music_emu, &info, gme->track_index))
64cabdff1aSopenharmony_ci        return AVERROR_STREAM_NOT_FOUND;
65cabdff1aSopenharmony_ci
66cabdff1aSopenharmony_ci    *duration = info->length;
67cabdff1aSopenharmony_ci    add_meta(s, "system",       info->system);
68cabdff1aSopenharmony_ci    add_meta(s, "game",         info->game);
69cabdff1aSopenharmony_ci    add_meta(s, "song",         info->song);
70cabdff1aSopenharmony_ci    add_meta(s, "author",       info->author);
71cabdff1aSopenharmony_ci    add_meta(s, "copyright",    info->copyright);
72cabdff1aSopenharmony_ci    add_meta(s, "comment",      info->comment);
73cabdff1aSopenharmony_ci    add_meta(s, "dumper",       info->dumper);
74cabdff1aSopenharmony_ci
75cabdff1aSopenharmony_ci    snprintf(buf, sizeof(buf), "%d", (int)gme_track_count(gme->music_emu));
76cabdff1aSopenharmony_ci    add_meta(s, "tracks", buf);
77cabdff1aSopenharmony_ci    gme_free_info(info);
78cabdff1aSopenharmony_ci
79cabdff1aSopenharmony_ci    return 0;
80cabdff1aSopenharmony_ci}
81cabdff1aSopenharmony_ci
82cabdff1aSopenharmony_ci#define AUDIO_PKT_SIZE 512
83cabdff1aSopenharmony_ci
84cabdff1aSopenharmony_cistatic int read_close_gme(AVFormatContext *s)
85cabdff1aSopenharmony_ci{
86cabdff1aSopenharmony_ci    GMEContext *gme = s->priv_data;
87cabdff1aSopenharmony_ci    if (gme->music_emu)
88cabdff1aSopenharmony_ci        gme_delete(gme->music_emu);
89cabdff1aSopenharmony_ci    return 0;
90cabdff1aSopenharmony_ci}
91cabdff1aSopenharmony_ci
92cabdff1aSopenharmony_cistatic int read_header_gme(AVFormatContext *s)
93cabdff1aSopenharmony_ci{
94cabdff1aSopenharmony_ci    AVStream *st;
95cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
96cabdff1aSopenharmony_ci    GMEContext *gme = s->priv_data;
97cabdff1aSopenharmony_ci    int64_t sz = avio_size(pb);
98cabdff1aSopenharmony_ci    int64_t duration;
99cabdff1aSopenharmony_ci    char *buf;
100cabdff1aSopenharmony_ci    char dummy;
101cabdff1aSopenharmony_ci    int ret;
102cabdff1aSopenharmony_ci
103cabdff1aSopenharmony_ci    if (sz < 0) {
104cabdff1aSopenharmony_ci        av_log(s, AV_LOG_WARNING, "Could not determine file size\n");
105cabdff1aSopenharmony_ci        sz = gme->max_size;
106cabdff1aSopenharmony_ci    } else if (gme->max_size && sz > gme->max_size) {
107cabdff1aSopenharmony_ci        sz = gme->max_size;
108cabdff1aSopenharmony_ci    }
109cabdff1aSopenharmony_ci
110cabdff1aSopenharmony_ci    buf = av_malloc(sz);
111cabdff1aSopenharmony_ci    if (!buf)
112cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
113cabdff1aSopenharmony_ci    sz = avio_read(pb, buf, sz);
114cabdff1aSopenharmony_ci
115cabdff1aSopenharmony_ci    // Data left means our buffer (the max_size option) is too small
116cabdff1aSopenharmony_ci    if (avio_read(pb, &dummy, 1) == 1) {
117cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "File size is larger than max_size option "
118cabdff1aSopenharmony_ci               "value %"PRIi64", consider increasing the max_size option\n",
119cabdff1aSopenharmony_ci               gme->max_size);
120cabdff1aSopenharmony_ci        av_freep(&buf);
121cabdff1aSopenharmony_ci        return AVERROR_BUFFER_TOO_SMALL;
122cabdff1aSopenharmony_ci    }
123cabdff1aSopenharmony_ci
124cabdff1aSopenharmony_ci    if (gme_open_data(buf, sz, &gme->music_emu, gme->sample_rate)) {
125cabdff1aSopenharmony_ci        gme->music_emu = NULL; /* Just for safety */
126cabdff1aSopenharmony_ci        av_freep(&buf);
127cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
128cabdff1aSopenharmony_ci    }
129cabdff1aSopenharmony_ci    av_freep(&buf);
130cabdff1aSopenharmony_ci
131cabdff1aSopenharmony_ci    ret = load_metadata(s, &duration);
132cabdff1aSopenharmony_ci    if (ret < 0)
133cabdff1aSopenharmony_ci        return ret;
134cabdff1aSopenharmony_ci    if (gme_start_track(gme->music_emu, gme->track_index))
135cabdff1aSopenharmony_ci        return AVERROR_UNKNOWN;
136cabdff1aSopenharmony_ci
137cabdff1aSopenharmony_ci    st = avformat_new_stream(s, NULL);
138cabdff1aSopenharmony_ci    if (!st)
139cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
140cabdff1aSopenharmony_ci    avpriv_set_pts_info(st, 64, 1, 1000);
141cabdff1aSopenharmony_ci    if (duration > 0)
142cabdff1aSopenharmony_ci        st->duration = duration;
143cabdff1aSopenharmony_ci    st->codecpar->codec_type  = AVMEDIA_TYPE_AUDIO;
144cabdff1aSopenharmony_ci    st->codecpar->codec_id    = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE);
145cabdff1aSopenharmony_ci    st->codecpar->ch_layout.nb_channels = 2;
146cabdff1aSopenharmony_ci    st->codecpar->sample_rate = gme->sample_rate;
147cabdff1aSopenharmony_ci
148cabdff1aSopenharmony_ci    return 0;
149cabdff1aSopenharmony_ci}
150cabdff1aSopenharmony_ci
151cabdff1aSopenharmony_cistatic int read_packet_gme(AVFormatContext *s, AVPacket *pkt)
152cabdff1aSopenharmony_ci{
153cabdff1aSopenharmony_ci    GMEContext *gme = s->priv_data;
154cabdff1aSopenharmony_ci    int n_samples = AUDIO_PKT_SIZE / 2;
155cabdff1aSopenharmony_ci    int ret;
156cabdff1aSopenharmony_ci
157cabdff1aSopenharmony_ci    if (gme_track_ended(gme->music_emu))
158cabdff1aSopenharmony_ci        return AVERROR_EOF;
159cabdff1aSopenharmony_ci
160cabdff1aSopenharmony_ci    if ((ret = av_new_packet(pkt, AUDIO_PKT_SIZE)) < 0)
161cabdff1aSopenharmony_ci        return ret;
162cabdff1aSopenharmony_ci
163cabdff1aSopenharmony_ci    if (gme_play(gme->music_emu, n_samples, (short *)pkt->data))
164cabdff1aSopenharmony_ci        return AVERROR_EXTERNAL;
165cabdff1aSopenharmony_ci
166cabdff1aSopenharmony_ci    return 0;
167cabdff1aSopenharmony_ci}
168cabdff1aSopenharmony_ci
169cabdff1aSopenharmony_cistatic int read_seek_gme(AVFormatContext *s, int stream_idx, int64_t ts, int flags)
170cabdff1aSopenharmony_ci{
171cabdff1aSopenharmony_ci    GMEContext *gme = s->priv_data;
172cabdff1aSopenharmony_ci    if (!gme_seek(gme->music_emu, (int)ts))
173cabdff1aSopenharmony_ci        return AVERROR_EXTERNAL;
174cabdff1aSopenharmony_ci    return 0;
175cabdff1aSopenharmony_ci}
176cabdff1aSopenharmony_ci
177cabdff1aSopenharmony_cistatic int probe_gme(const AVProbeData *p)
178cabdff1aSopenharmony_ci{
179cabdff1aSopenharmony_ci    // Reads 4 bytes - returns "" if unknown format.
180cabdff1aSopenharmony_ci    if (gme_identify_header(p->buf)[0]) {
181cabdff1aSopenharmony_ci        if (p->buf_size < 16384)
182cabdff1aSopenharmony_ci            return AVPROBE_SCORE_MAX / 4 ;
183cabdff1aSopenharmony_ci        else
184cabdff1aSopenharmony_ci            return AVPROBE_SCORE_MAX / 2;
185cabdff1aSopenharmony_ci    }
186cabdff1aSopenharmony_ci    return 0;
187cabdff1aSopenharmony_ci}
188cabdff1aSopenharmony_ci
189cabdff1aSopenharmony_cistatic const AVClass class_gme = {
190cabdff1aSopenharmony_ci    .class_name = "Game Music Emu demuxer",
191cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
192cabdff1aSopenharmony_ci    .option     = options,
193cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
194cabdff1aSopenharmony_ci};
195cabdff1aSopenharmony_ci
196cabdff1aSopenharmony_ciconst AVInputFormat ff_libgme_demuxer = {
197cabdff1aSopenharmony_ci    .name           = "libgme",
198cabdff1aSopenharmony_ci    .long_name      = NULL_IF_CONFIG_SMALL("Game Music Emu demuxer"),
199cabdff1aSopenharmony_ci    .priv_data_size = sizeof(GMEContext),
200cabdff1aSopenharmony_ci    .flags_internal = FF_FMT_INIT_CLEANUP,
201cabdff1aSopenharmony_ci    .read_probe     = probe_gme,
202cabdff1aSopenharmony_ci    .read_header    = read_header_gme,
203cabdff1aSopenharmony_ci    .read_packet    = read_packet_gme,
204cabdff1aSopenharmony_ci    .read_close     = read_close_gme,
205cabdff1aSopenharmony_ci    .read_seek      = read_seek_gme,
206cabdff1aSopenharmony_ci    .priv_class     = &class_gme,
207cabdff1aSopenharmony_ci};
208