1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * FSB demuxer
3cabdff1aSopenharmony_ci * Copyright (c) 2015 Paul B Mahol
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * This file is part of FFmpeg.
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
11cabdff1aSopenharmony_ci *
12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15cabdff1aSopenharmony_ci * Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci
22cabdff1aSopenharmony_ci#include "libavutil/avassert.h"
23cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
24cabdff1aSopenharmony_ci#include "avformat.h"
25cabdff1aSopenharmony_ci#include "avio.h"
26cabdff1aSopenharmony_ci#include "internal.h"
27cabdff1aSopenharmony_ci
28cabdff1aSopenharmony_cistatic int fsb_probe(const AVProbeData *p)
29cabdff1aSopenharmony_ci{
30cabdff1aSopenharmony_ci    if (memcmp(p->buf, "FSB", 3) || p->buf[3] - '0' < 1 || p->buf[3] - '0' > 5)
31cabdff1aSopenharmony_ci        return 0;
32cabdff1aSopenharmony_ci    if (AV_RL32(p->buf + 4) != 1)
33cabdff1aSopenharmony_ci        return 0;
34cabdff1aSopenharmony_ci    return AVPROBE_SCORE_MAX;
35cabdff1aSopenharmony_ci}
36cabdff1aSopenharmony_ci
37cabdff1aSopenharmony_cistatic int fsb_read_header(AVFormatContext *s)
38cabdff1aSopenharmony_ci{
39cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
40cabdff1aSopenharmony_ci    unsigned format, version, c;
41cabdff1aSopenharmony_ci    int64_t offset;
42cabdff1aSopenharmony_ci    AVCodecParameters *par;
43cabdff1aSopenharmony_ci    AVStream *st = avformat_new_stream(s, NULL);
44cabdff1aSopenharmony_ci    int ret;
45cabdff1aSopenharmony_ci
46cabdff1aSopenharmony_ci    avio_skip(pb, 3); // "FSB"
47cabdff1aSopenharmony_ci    version = avio_r8(pb) - '0';
48cabdff1aSopenharmony_ci    if (version != 4 && version != 3) {
49cabdff1aSopenharmony_ci        avpriv_request_sample(s, "version %d", version);
50cabdff1aSopenharmony_ci        return AVERROR_PATCHWELCOME;
51cabdff1aSopenharmony_ci    }
52cabdff1aSopenharmony_ci
53cabdff1aSopenharmony_ci    avio_skip(pb, 4);
54cabdff1aSopenharmony_ci
55cabdff1aSopenharmony_ci    if (!st)
56cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
57cabdff1aSopenharmony_ci    par = st->codecpar;
58cabdff1aSopenharmony_ci    par->codec_type  = AVMEDIA_TYPE_AUDIO;
59cabdff1aSopenharmony_ci    par->codec_tag   = 0;
60cabdff1aSopenharmony_ci
61cabdff1aSopenharmony_ci    if (version == 3) {
62cabdff1aSopenharmony_ci        offset = avio_rl32(pb) + 0x18;
63cabdff1aSopenharmony_ci        avio_skip(pb, 44);
64cabdff1aSopenharmony_ci        st->duration = avio_rl32(pb);
65cabdff1aSopenharmony_ci        avio_skip(pb, 12);
66cabdff1aSopenharmony_ci        format = avio_rl32(pb);
67cabdff1aSopenharmony_ci        par->sample_rate = avio_rl32(pb);
68cabdff1aSopenharmony_ci        if (par->sample_rate <= 0)
69cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
70cabdff1aSopenharmony_ci        avio_skip(pb, 6);
71cabdff1aSopenharmony_ci        par->ch_layout.nb_channels = avio_rl16(pb);
72cabdff1aSopenharmony_ci        if (!par->ch_layout.nb_channels)
73cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
74cabdff1aSopenharmony_ci
75cabdff1aSopenharmony_ci        if (format & 0x00000100) {
76cabdff1aSopenharmony_ci            par->codec_id    = AV_CODEC_ID_PCM_S16LE;
77cabdff1aSopenharmony_ci            par->block_align = 4096 * par->ch_layout.nb_channels;
78cabdff1aSopenharmony_ci        } else if (format & 0x00400000) {
79cabdff1aSopenharmony_ci            par->bits_per_coded_sample = 4;
80cabdff1aSopenharmony_ci            par->codec_id    = AV_CODEC_ID_ADPCM_IMA_WAV;
81cabdff1aSopenharmony_ci            par->block_align = 36 * par->ch_layout.nb_channels;
82cabdff1aSopenharmony_ci        } else if (format & 0x00800000) {
83cabdff1aSopenharmony_ci            par->codec_id    = AV_CODEC_ID_ADPCM_PSX;
84cabdff1aSopenharmony_ci            par->block_align = 16 * par->ch_layout.nb_channels;
85cabdff1aSopenharmony_ci        } else if (format & 0x02000000) {
86cabdff1aSopenharmony_ci            par->codec_id    = AV_CODEC_ID_ADPCM_THP;
87cabdff1aSopenharmony_ci            par->block_align = 8 * par->ch_layout.nb_channels;
88cabdff1aSopenharmony_ci            if (par->ch_layout.nb_channels > INT_MAX / 32)
89cabdff1aSopenharmony_ci                return AVERROR_INVALIDDATA;
90cabdff1aSopenharmony_ci            ret = ff_alloc_extradata(par, 32 * par->ch_layout.nb_channels);
91cabdff1aSopenharmony_ci            if (ret < 0)
92cabdff1aSopenharmony_ci                return ret;
93cabdff1aSopenharmony_ci            avio_seek(pb, 0x68, SEEK_SET);
94cabdff1aSopenharmony_ci            for (c = 0; c < par->ch_layout.nb_channels; c++) {
95cabdff1aSopenharmony_ci                avio_read(pb, par->extradata + 32 * c, 32);
96cabdff1aSopenharmony_ci                avio_skip(pb, 14);
97cabdff1aSopenharmony_ci            }
98cabdff1aSopenharmony_ci        } else {
99cabdff1aSopenharmony_ci            avpriv_request_sample(s, "format 0x%X", format);
100cabdff1aSopenharmony_ci            return AVERROR_PATCHWELCOME;
101cabdff1aSopenharmony_ci        }
102cabdff1aSopenharmony_ci    } else if (version == 4) {
103cabdff1aSopenharmony_ci        offset = avio_rl32(pb) + 0x30;
104cabdff1aSopenharmony_ci        avio_skip(pb, 80);
105cabdff1aSopenharmony_ci        st->duration = avio_rl32(pb);
106cabdff1aSopenharmony_ci
107cabdff1aSopenharmony_ci        format = avio_rb32(pb);
108cabdff1aSopenharmony_ci        switch(format) {
109cabdff1aSopenharmony_ci        case 0x40001001:
110cabdff1aSopenharmony_ci        case 0x00001005:
111cabdff1aSopenharmony_ci        case 0x40001081:
112cabdff1aSopenharmony_ci        case 0x40200001:
113cabdff1aSopenharmony_ci            par->codec_id = AV_CODEC_ID_XMA2;
114cabdff1aSopenharmony_ci            break;
115cabdff1aSopenharmony_ci        case 0x40000802:
116cabdff1aSopenharmony_ci            par->codec_id = AV_CODEC_ID_ADPCM_THP;
117cabdff1aSopenharmony_ci            break;
118cabdff1aSopenharmony_ci        default:
119cabdff1aSopenharmony_ci            avpriv_request_sample(s, "format 0x%X", format);
120cabdff1aSopenharmony_ci            return AVERROR_PATCHWELCOME;
121cabdff1aSopenharmony_ci        }
122cabdff1aSopenharmony_ci
123cabdff1aSopenharmony_ci        par->sample_rate = avio_rl32(pb);
124cabdff1aSopenharmony_ci        if (par->sample_rate <= 0)
125cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
126cabdff1aSopenharmony_ci        avio_skip(pb, 6);
127cabdff1aSopenharmony_ci
128cabdff1aSopenharmony_ci        par->ch_layout.nb_channels = avio_rl16(pb);
129cabdff1aSopenharmony_ci        if (!par->ch_layout.nb_channels)
130cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
131cabdff1aSopenharmony_ci
132cabdff1aSopenharmony_ci        switch (par->codec_id) {
133cabdff1aSopenharmony_ci        case AV_CODEC_ID_XMA2:
134cabdff1aSopenharmony_ci            ret = ff_alloc_extradata(par, 34);
135cabdff1aSopenharmony_ci            if (ret < 0)
136cabdff1aSopenharmony_ci                return ret;
137cabdff1aSopenharmony_ci            memset(par->extradata, 0, 34);
138cabdff1aSopenharmony_ci            par->block_align = 2048;
139cabdff1aSopenharmony_ci            break;
140cabdff1aSopenharmony_ci        case AV_CODEC_ID_ADPCM_THP:
141cabdff1aSopenharmony_ci            if (par->ch_layout.nb_channels > INT_MAX / 32)
142cabdff1aSopenharmony_ci                return AVERROR_INVALIDDATA;
143cabdff1aSopenharmony_ci            ret = ff_alloc_extradata(par, 32 * par->ch_layout.nb_channels);
144cabdff1aSopenharmony_ci            if (ret < 0)
145cabdff1aSopenharmony_ci                return ret;
146cabdff1aSopenharmony_ci            avio_seek(pb, 0x80, SEEK_SET);
147cabdff1aSopenharmony_ci            for (c = 0; c < par->ch_layout.nb_channels; c++) {
148cabdff1aSopenharmony_ci                avio_read(pb, par->extradata + 32 * c, 32);
149cabdff1aSopenharmony_ci                avio_skip(pb, 14);
150cabdff1aSopenharmony_ci            }
151cabdff1aSopenharmony_ci            par->block_align = 8 * par->ch_layout.nb_channels;
152cabdff1aSopenharmony_ci            break;
153cabdff1aSopenharmony_ci        }
154cabdff1aSopenharmony_ci    } else {
155cabdff1aSopenharmony_ci        av_assert0(0);
156cabdff1aSopenharmony_ci    }
157cabdff1aSopenharmony_ci
158cabdff1aSopenharmony_ci    avio_skip(pb, offset - avio_tell(pb));
159cabdff1aSopenharmony_ci    ffformatcontext(s)->data_offset = avio_tell(pb);
160cabdff1aSopenharmony_ci
161cabdff1aSopenharmony_ci    avpriv_set_pts_info(st, 64, 1, par->sample_rate);
162cabdff1aSopenharmony_ci
163cabdff1aSopenharmony_ci    return 0;
164cabdff1aSopenharmony_ci}
165cabdff1aSopenharmony_ci
166cabdff1aSopenharmony_cistatic int fsb_read_packet(AVFormatContext *s, AVPacket *pkt)
167cabdff1aSopenharmony_ci{
168cabdff1aSopenharmony_ci    AVCodecParameters *par = s->streams[0]->codecpar;
169cabdff1aSopenharmony_ci    int64_t pos;
170cabdff1aSopenharmony_ci    int ret;
171cabdff1aSopenharmony_ci
172cabdff1aSopenharmony_ci    if (avio_feof(s->pb))
173cabdff1aSopenharmony_ci        return AVERROR_EOF;
174cabdff1aSopenharmony_ci
175cabdff1aSopenharmony_ci    pos = avio_tell(s->pb);
176cabdff1aSopenharmony_ci    if (par->codec_id == AV_CODEC_ID_ADPCM_THP &&
177cabdff1aSopenharmony_ci        par->ch_layout.nb_channels > 1) {
178cabdff1aSopenharmony_ci        int i, ch;
179cabdff1aSopenharmony_ci
180cabdff1aSopenharmony_ci        ret = av_new_packet(pkt, par->block_align);
181cabdff1aSopenharmony_ci        if (ret < 0)
182cabdff1aSopenharmony_ci            return ret;
183cabdff1aSopenharmony_ci        for (i = 0; i < 4; i++) {
184cabdff1aSopenharmony_ci            for (ch = 0; ch < par->ch_layout.nb_channels; ch++) {
185cabdff1aSopenharmony_ci                pkt->data[ch * 8 + i * 2 + 0] = avio_r8(s->pb);
186cabdff1aSopenharmony_ci                pkt->data[ch * 8 + i * 2 + 1] = avio_r8(s->pb);
187cabdff1aSopenharmony_ci            }
188cabdff1aSopenharmony_ci        }
189cabdff1aSopenharmony_ci        ret = 0;
190cabdff1aSopenharmony_ci    } else {
191cabdff1aSopenharmony_ci        ret = av_get_packet(s->pb, pkt, par->block_align);
192cabdff1aSopenharmony_ci    }
193cabdff1aSopenharmony_ci
194cabdff1aSopenharmony_ci    if (par->codec_id == AV_CODEC_ID_XMA2 && pkt->size >= 1)
195cabdff1aSopenharmony_ci        pkt->duration = (pkt->data[0] >> 2) * 512;
196cabdff1aSopenharmony_ci
197cabdff1aSopenharmony_ci    pkt->pos = pos;
198cabdff1aSopenharmony_ci    pkt->stream_index = 0;
199cabdff1aSopenharmony_ci
200cabdff1aSopenharmony_ci    return ret;
201cabdff1aSopenharmony_ci}
202cabdff1aSopenharmony_ci
203cabdff1aSopenharmony_ciconst AVInputFormat ff_fsb_demuxer = {
204cabdff1aSopenharmony_ci    .name        = "fsb",
205cabdff1aSopenharmony_ci    .long_name   = NULL_IF_CONFIG_SMALL("FMOD Sample Bank"),
206cabdff1aSopenharmony_ci    .read_probe  = fsb_probe,
207cabdff1aSopenharmony_ci    .read_header = fsb_read_header,
208cabdff1aSopenharmony_ci    .read_packet = fsb_read_packet,
209cabdff1aSopenharmony_ci    .extensions  = "fsb",
210cabdff1aSopenharmony_ci    .flags       = AVFMT_GENERIC_INDEX,
211cabdff1aSopenharmony_ci};
212