1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * id RoQ (.roq) File Demuxer
3cabdff1aSopenharmony_ci * Copyright (c) 2003 The FFmpeg project
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/**
23cabdff1aSopenharmony_ci * @file
24cabdff1aSopenharmony_ci * id RoQ format file demuxer
25cabdff1aSopenharmony_ci * by Mike Melanson (melanson@pcisys.net)
26cabdff1aSopenharmony_ci * for more information on the .roq file format, visit:
27cabdff1aSopenharmony_ci *   http://www.csse.monash.edu.au/~timf/
28cabdff1aSopenharmony_ci */
29cabdff1aSopenharmony_ci
30cabdff1aSopenharmony_ci#include "libavutil/channel_layout.h"
31cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
32cabdff1aSopenharmony_ci#include "avformat.h"
33cabdff1aSopenharmony_ci#include "internal.h"
34cabdff1aSopenharmony_ci#include "avio_internal.h"
35cabdff1aSopenharmony_ci
36cabdff1aSopenharmony_ci#define RoQ_MAGIC_NUMBER 0x1084
37cabdff1aSopenharmony_ci#define RoQ_CHUNK_PREAMBLE_SIZE 8
38cabdff1aSopenharmony_ci#define RoQ_AUDIO_SAMPLE_RATE 22050
39cabdff1aSopenharmony_ci#define RoQ_CHUNKS_TO_SCAN 30
40cabdff1aSopenharmony_ci
41cabdff1aSopenharmony_ci#define RoQ_INFO           0x1001
42cabdff1aSopenharmony_ci#define RoQ_QUAD_CODEBOOK  0x1002
43cabdff1aSopenharmony_ci#define RoQ_QUAD_VQ        0x1011
44cabdff1aSopenharmony_ci#define RoQ_SOUND_MONO     0x1020
45cabdff1aSopenharmony_ci#define RoQ_SOUND_STEREO   0x1021
46cabdff1aSopenharmony_ci
47cabdff1aSopenharmony_citypedef struct RoqDemuxContext {
48cabdff1aSopenharmony_ci
49cabdff1aSopenharmony_ci    int frame_rate;
50cabdff1aSopenharmony_ci    int width;
51cabdff1aSopenharmony_ci    int height;
52cabdff1aSopenharmony_ci    int audio_channels;
53cabdff1aSopenharmony_ci
54cabdff1aSopenharmony_ci    int video_stream_index;
55cabdff1aSopenharmony_ci    int audio_stream_index;
56cabdff1aSopenharmony_ci
57cabdff1aSopenharmony_ci    int64_t video_pts;
58cabdff1aSopenharmony_ci    unsigned int audio_frame_count;
59cabdff1aSopenharmony_ci
60cabdff1aSopenharmony_ci} RoqDemuxContext;
61cabdff1aSopenharmony_ci
62cabdff1aSopenharmony_cistatic int roq_probe(const AVProbeData *p)
63cabdff1aSopenharmony_ci{
64cabdff1aSopenharmony_ci    if ((AV_RL16(&p->buf[0]) != RoQ_MAGIC_NUMBER) ||
65cabdff1aSopenharmony_ci        (AV_RL32(&p->buf[2]) != 0xFFFFFFFF))
66cabdff1aSopenharmony_ci        return 0;
67cabdff1aSopenharmony_ci
68cabdff1aSopenharmony_ci    return AVPROBE_SCORE_MAX;
69cabdff1aSopenharmony_ci}
70cabdff1aSopenharmony_ci
71cabdff1aSopenharmony_cistatic int roq_read_header(AVFormatContext *s)
72cabdff1aSopenharmony_ci{
73cabdff1aSopenharmony_ci    RoqDemuxContext *roq = s->priv_data;
74cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
75cabdff1aSopenharmony_ci    unsigned char preamble[RoQ_CHUNK_PREAMBLE_SIZE];
76cabdff1aSopenharmony_ci
77cabdff1aSopenharmony_ci    /* get the main header */
78cabdff1aSopenharmony_ci    if (avio_read(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) !=
79cabdff1aSopenharmony_ci        RoQ_CHUNK_PREAMBLE_SIZE)
80cabdff1aSopenharmony_ci        return AVERROR(EIO);
81cabdff1aSopenharmony_ci    roq->frame_rate = AV_RL16(&preamble[6]);
82cabdff1aSopenharmony_ci
83cabdff1aSopenharmony_ci    /* init private context parameters */
84cabdff1aSopenharmony_ci    roq->width = roq->height = roq->audio_channels = roq->video_pts =
85cabdff1aSopenharmony_ci    roq->audio_frame_count = 0;
86cabdff1aSopenharmony_ci    roq->audio_stream_index = -1;
87cabdff1aSopenharmony_ci    roq->video_stream_index = -1;
88cabdff1aSopenharmony_ci
89cabdff1aSopenharmony_ci    s->ctx_flags |= AVFMTCTX_NOHEADER;
90cabdff1aSopenharmony_ci
91cabdff1aSopenharmony_ci    return 0;
92cabdff1aSopenharmony_ci}
93cabdff1aSopenharmony_ci
94cabdff1aSopenharmony_cistatic int roq_read_packet(AVFormatContext *s,
95cabdff1aSopenharmony_ci                           AVPacket *pkt)
96cabdff1aSopenharmony_ci{
97cabdff1aSopenharmony_ci    RoqDemuxContext *roq = s->priv_data;
98cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
99cabdff1aSopenharmony_ci    int ret = 0;
100cabdff1aSopenharmony_ci    unsigned int chunk_size;
101cabdff1aSopenharmony_ci    unsigned int chunk_type;
102cabdff1aSopenharmony_ci    unsigned int codebook_size;
103cabdff1aSopenharmony_ci    unsigned char preamble[RoQ_CHUNK_PREAMBLE_SIZE];
104cabdff1aSopenharmony_ci    int packet_read = 0;
105cabdff1aSopenharmony_ci    int64_t codebook_offset;
106cabdff1aSopenharmony_ci
107cabdff1aSopenharmony_ci    while (!packet_read) {
108cabdff1aSopenharmony_ci
109cabdff1aSopenharmony_ci        if (avio_feof(s->pb))
110cabdff1aSopenharmony_ci            return AVERROR(EIO);
111cabdff1aSopenharmony_ci
112cabdff1aSopenharmony_ci        /* get the next chunk preamble */
113cabdff1aSopenharmony_ci        if ((ret = avio_read(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE)) !=
114cabdff1aSopenharmony_ci            RoQ_CHUNK_PREAMBLE_SIZE)
115cabdff1aSopenharmony_ci            return AVERROR(EIO);
116cabdff1aSopenharmony_ci
117cabdff1aSopenharmony_ci        chunk_type = AV_RL16(&preamble[0]);
118cabdff1aSopenharmony_ci        chunk_size = AV_RL32(&preamble[2]);
119cabdff1aSopenharmony_ci        if(chunk_size > INT_MAX)
120cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
121cabdff1aSopenharmony_ci
122cabdff1aSopenharmony_ci        chunk_size = ffio_limit(pb, chunk_size);
123cabdff1aSopenharmony_ci
124cabdff1aSopenharmony_ci        switch (chunk_type) {
125cabdff1aSopenharmony_ci
126cabdff1aSopenharmony_ci        case RoQ_INFO:
127cabdff1aSopenharmony_ci            if (roq->video_stream_index == -1) {
128cabdff1aSopenharmony_ci                AVStream *st = avformat_new_stream(s, NULL);
129cabdff1aSopenharmony_ci                if (!st)
130cabdff1aSopenharmony_ci                    return AVERROR(ENOMEM);
131cabdff1aSopenharmony_ci                avpriv_set_pts_info(st, 63, 1, roq->frame_rate);
132cabdff1aSopenharmony_ci                roq->video_stream_index = st->index;
133cabdff1aSopenharmony_ci                st->codecpar->codec_type   = AVMEDIA_TYPE_VIDEO;
134cabdff1aSopenharmony_ci                st->codecpar->codec_id     = AV_CODEC_ID_ROQ;
135cabdff1aSopenharmony_ci                st->codecpar->codec_tag    = 0;  /* no fourcc */
136cabdff1aSopenharmony_ci
137cabdff1aSopenharmony_ci                if (avio_read(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) != RoQ_CHUNK_PREAMBLE_SIZE)
138cabdff1aSopenharmony_ci                    return AVERROR(EIO);
139cabdff1aSopenharmony_ci                st->codecpar->width  = roq->width  = AV_RL16(preamble);
140cabdff1aSopenharmony_ci                st->codecpar->height = roq->height = AV_RL16(preamble + 2);
141cabdff1aSopenharmony_ci                break;
142cabdff1aSopenharmony_ci            }
143cabdff1aSopenharmony_ci            /* don't care about this chunk anymore */
144cabdff1aSopenharmony_ci            avio_skip(pb, RoQ_CHUNK_PREAMBLE_SIZE);
145cabdff1aSopenharmony_ci            break;
146cabdff1aSopenharmony_ci
147cabdff1aSopenharmony_ci        case RoQ_QUAD_CODEBOOK:
148cabdff1aSopenharmony_ci            if (roq->video_stream_index < 0)
149cabdff1aSopenharmony_ci                return AVERROR_INVALIDDATA;
150cabdff1aSopenharmony_ci            /* packet needs to contain both this codebook and next VQ chunk */
151cabdff1aSopenharmony_ci            codebook_offset = avio_tell(pb) - RoQ_CHUNK_PREAMBLE_SIZE;
152cabdff1aSopenharmony_ci            codebook_size = chunk_size;
153cabdff1aSopenharmony_ci            avio_skip(pb, codebook_size);
154cabdff1aSopenharmony_ci            if (avio_read(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) !=
155cabdff1aSopenharmony_ci                RoQ_CHUNK_PREAMBLE_SIZE)
156cabdff1aSopenharmony_ci                return AVERROR(EIO);
157cabdff1aSopenharmony_ci            chunk_size = AV_RL32(&preamble[2]) + RoQ_CHUNK_PREAMBLE_SIZE * 2 +
158cabdff1aSopenharmony_ci                codebook_size;
159cabdff1aSopenharmony_ci
160cabdff1aSopenharmony_ci            if (chunk_size > INT_MAX)
161cabdff1aSopenharmony_ci                return AVERROR_INVALIDDATA;
162cabdff1aSopenharmony_ci
163cabdff1aSopenharmony_ci            /* rewind */
164cabdff1aSopenharmony_ci            avio_seek(pb, codebook_offset, SEEK_SET);
165cabdff1aSopenharmony_ci
166cabdff1aSopenharmony_ci            /* load up the packet */
167cabdff1aSopenharmony_ci            ret= av_get_packet(pb, pkt, chunk_size);
168cabdff1aSopenharmony_ci            if (ret != chunk_size)
169cabdff1aSopenharmony_ci                return AVERROR(EIO);
170cabdff1aSopenharmony_ci            pkt->stream_index = roq->video_stream_index;
171cabdff1aSopenharmony_ci            pkt->pts = roq->video_pts++;
172cabdff1aSopenharmony_ci
173cabdff1aSopenharmony_ci            packet_read = 1;
174cabdff1aSopenharmony_ci            break;
175cabdff1aSopenharmony_ci
176cabdff1aSopenharmony_ci        case RoQ_SOUND_MONO:
177cabdff1aSopenharmony_ci        case RoQ_SOUND_STEREO:
178cabdff1aSopenharmony_ci            if (roq->audio_stream_index == -1) {
179cabdff1aSopenharmony_ci                AVStream *st = avformat_new_stream(s, NULL);
180cabdff1aSopenharmony_ci                if (!st)
181cabdff1aSopenharmony_ci                    return AVERROR(ENOMEM);
182cabdff1aSopenharmony_ci                avpriv_set_pts_info(st, 32, 1, RoQ_AUDIO_SAMPLE_RATE);
183cabdff1aSopenharmony_ci                roq->audio_stream_index = st->index;
184cabdff1aSopenharmony_ci                st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
185cabdff1aSopenharmony_ci                st->codecpar->codec_id = AV_CODEC_ID_ROQ_DPCM;
186cabdff1aSopenharmony_ci                st->codecpar->codec_tag = 0;  /* no tag */
187cabdff1aSopenharmony_ci                if (chunk_type == RoQ_SOUND_STEREO) {
188cabdff1aSopenharmony_ci                    st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;
189cabdff1aSopenharmony_ci                } else {
190cabdff1aSopenharmony_ci                    st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
191cabdff1aSopenharmony_ci                }
192cabdff1aSopenharmony_ci                roq->audio_channels = st->codecpar->ch_layout.nb_channels;
193cabdff1aSopenharmony_ci                st->codecpar->sample_rate = RoQ_AUDIO_SAMPLE_RATE;
194cabdff1aSopenharmony_ci                st->codecpar->bits_per_coded_sample = 16;
195cabdff1aSopenharmony_ci                st->codecpar->bit_rate = roq->audio_channels * st->codecpar->sample_rate *
196cabdff1aSopenharmony_ci                    st->codecpar->bits_per_coded_sample;
197cabdff1aSopenharmony_ci                st->codecpar->block_align = roq->audio_channels * st->codecpar->bits_per_coded_sample;
198cabdff1aSopenharmony_ci            }
199cabdff1aSopenharmony_ci        case RoQ_QUAD_VQ:
200cabdff1aSopenharmony_ci            if (chunk_type == RoQ_QUAD_VQ) {
201cabdff1aSopenharmony_ci                if (roq->video_stream_index < 0)
202cabdff1aSopenharmony_ci                    return AVERROR_INVALIDDATA;
203cabdff1aSopenharmony_ci            }
204cabdff1aSopenharmony_ci
205cabdff1aSopenharmony_ci            /* load up the packet */
206cabdff1aSopenharmony_ci            ret = av_new_packet(pkt, chunk_size + RoQ_CHUNK_PREAMBLE_SIZE);
207cabdff1aSopenharmony_ci            if (ret < 0)
208cabdff1aSopenharmony_ci                return ret;
209cabdff1aSopenharmony_ci            /* copy over preamble */
210cabdff1aSopenharmony_ci            memcpy(pkt->data, preamble, RoQ_CHUNK_PREAMBLE_SIZE);
211cabdff1aSopenharmony_ci
212cabdff1aSopenharmony_ci            if (chunk_type == RoQ_QUAD_VQ) {
213cabdff1aSopenharmony_ci                pkt->stream_index = roq->video_stream_index;
214cabdff1aSopenharmony_ci                pkt->pts = roq->video_pts++;
215cabdff1aSopenharmony_ci            } else {
216cabdff1aSopenharmony_ci                pkt->stream_index = roq->audio_stream_index;
217cabdff1aSopenharmony_ci                pkt->pts = roq->audio_frame_count;
218cabdff1aSopenharmony_ci                roq->audio_frame_count += (chunk_size / roq->audio_channels);
219cabdff1aSopenharmony_ci            }
220cabdff1aSopenharmony_ci
221cabdff1aSopenharmony_ci            pkt->pos= avio_tell(pb);
222cabdff1aSopenharmony_ci            ret = avio_read(pb, pkt->data + RoQ_CHUNK_PREAMBLE_SIZE,
223cabdff1aSopenharmony_ci                chunk_size);
224cabdff1aSopenharmony_ci            if (ret != chunk_size) {
225cabdff1aSopenharmony_ci                return AVERROR(EIO);
226cabdff1aSopenharmony_ci            }
227cabdff1aSopenharmony_ci
228cabdff1aSopenharmony_ci            packet_read = 1;
229cabdff1aSopenharmony_ci            break;
230cabdff1aSopenharmony_ci
231cabdff1aSopenharmony_ci        default:
232cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "  unknown RoQ chunk (%04X)\n", chunk_type);
233cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
234cabdff1aSopenharmony_ci        }
235cabdff1aSopenharmony_ci    }
236cabdff1aSopenharmony_ci
237cabdff1aSopenharmony_ci    return ret;
238cabdff1aSopenharmony_ci}
239cabdff1aSopenharmony_ci
240cabdff1aSopenharmony_ciconst AVInputFormat ff_roq_demuxer = {
241cabdff1aSopenharmony_ci    .name           = "roq",
242cabdff1aSopenharmony_ci    .long_name      = NULL_IF_CONFIG_SMALL("id RoQ"),
243cabdff1aSopenharmony_ci    .priv_data_size = sizeof(RoqDemuxContext),
244cabdff1aSopenharmony_ci    .read_probe     = roq_probe,
245cabdff1aSopenharmony_ci    .read_header    = roq_read_header,
246cabdff1aSopenharmony_ci    .read_packet    = roq_read_packet,
247cabdff1aSopenharmony_ci};
248