1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Gremlin Digital Video demuxer
3cabdff1aSopenharmony_ci * Copyright (c) 2017 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/intreadwrite.h"
23cabdff1aSopenharmony_ci
24cabdff1aSopenharmony_ci#include "avformat.h"
25cabdff1aSopenharmony_ci#include "avio.h"
26cabdff1aSopenharmony_ci#include "internal.h"
27cabdff1aSopenharmony_ci
28cabdff1aSopenharmony_citypedef struct GDVContext {
29cabdff1aSopenharmony_ci    int is_first_video;
30cabdff1aSopenharmony_ci    int is_audio;
31cabdff1aSopenharmony_ci    int audio_size;
32cabdff1aSopenharmony_ci    int audio_stream_index;
33cabdff1aSopenharmony_ci    int video_stream_index;
34cabdff1aSopenharmony_ci    unsigned pal[256];
35cabdff1aSopenharmony_ci} GDVContext;
36cabdff1aSopenharmony_ci
37cabdff1aSopenharmony_cistatic int gdv_read_probe(const AVProbeData *p)
38cabdff1aSopenharmony_ci{
39cabdff1aSopenharmony_ci    if (AV_RL32(p->buf) == 0x29111994)
40cabdff1aSopenharmony_ci        return AVPROBE_SCORE_MAX;
41cabdff1aSopenharmony_ci
42cabdff1aSopenharmony_ci    return 0;
43cabdff1aSopenharmony_ci}
44cabdff1aSopenharmony_ci
45cabdff1aSopenharmony_cistatic struct {
46cabdff1aSopenharmony_ci    uint16_t id;
47cabdff1aSopenharmony_ci    uint16_t width;
48cabdff1aSopenharmony_ci    uint16_t height;
49cabdff1aSopenharmony_ci} FixedSize[] = {
50cabdff1aSopenharmony_ci    { 0, 320, 200},
51cabdff1aSopenharmony_ci    { 1, 640, 200},
52cabdff1aSopenharmony_ci    { 2, 320, 167},
53cabdff1aSopenharmony_ci    { 3, 320, 180},
54cabdff1aSopenharmony_ci    { 4, 320, 400},
55cabdff1aSopenharmony_ci    { 5, 320, 170},
56cabdff1aSopenharmony_ci    { 6, 160,  85},
57cabdff1aSopenharmony_ci    { 7, 160,  83},
58cabdff1aSopenharmony_ci    { 8, 160,  90},
59cabdff1aSopenharmony_ci    { 9, 280, 128},
60cabdff1aSopenharmony_ci    {10, 320, 240},
61cabdff1aSopenharmony_ci    {11, 320, 201},
62cabdff1aSopenharmony_ci    {16, 640, 400},
63cabdff1aSopenharmony_ci    {17, 640, 200},
64cabdff1aSopenharmony_ci    {18, 640, 180},
65cabdff1aSopenharmony_ci    {19, 640, 167},
66cabdff1aSopenharmony_ci    {20, 640, 170},
67cabdff1aSopenharmony_ci    {21, 320, 240}
68cabdff1aSopenharmony_ci};
69cabdff1aSopenharmony_ci
70cabdff1aSopenharmony_cistatic int gdv_read_header(AVFormatContext *ctx)
71cabdff1aSopenharmony_ci{
72cabdff1aSopenharmony_ci    GDVContext *gdv = ctx->priv_data;
73cabdff1aSopenharmony_ci    AVIOContext *pb = ctx->pb;
74cabdff1aSopenharmony_ci    AVStream *vst, *ast;
75cabdff1aSopenharmony_ci    unsigned fps, snd_flags, vid_depth, size_id;
76cabdff1aSopenharmony_ci
77cabdff1aSopenharmony_ci    avio_skip(pb, 4);
78cabdff1aSopenharmony_ci    size_id = avio_rl16(pb);
79cabdff1aSopenharmony_ci
80cabdff1aSopenharmony_ci    vst = avformat_new_stream(ctx, 0);
81cabdff1aSopenharmony_ci    if (!vst)
82cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
83cabdff1aSopenharmony_ci
84cabdff1aSopenharmony_ci    vst->start_time        = 0;
85cabdff1aSopenharmony_ci    vst->duration          =
86cabdff1aSopenharmony_ci    vst->nb_frames         = avio_rl16(pb);
87cabdff1aSopenharmony_ci
88cabdff1aSopenharmony_ci    fps = avio_rl16(pb);
89cabdff1aSopenharmony_ci    if (!fps)
90cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
91cabdff1aSopenharmony_ci
92cabdff1aSopenharmony_ci    snd_flags = avio_rl16(pb);
93cabdff1aSopenharmony_ci    if (snd_flags & 1) {
94cabdff1aSopenharmony_ci        ast = avformat_new_stream(ctx, 0);
95cabdff1aSopenharmony_ci        if (!ast)
96cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
97cabdff1aSopenharmony_ci
98cabdff1aSopenharmony_ci        ast->start_time = 0;
99cabdff1aSopenharmony_ci        ast->codecpar->codec_type  = AVMEDIA_TYPE_AUDIO;
100cabdff1aSopenharmony_ci        ast->codecpar->codec_tag   = 0;
101cabdff1aSopenharmony_ci        ast->codecpar->sample_rate = avio_rl16(pb);
102cabdff1aSopenharmony_ci        ast->codecpar->ch_layout.nb_channels = 1 + !!(snd_flags & 2);
103cabdff1aSopenharmony_ci        if (snd_flags & 8) {
104cabdff1aSopenharmony_ci            ast->codecpar->codec_id = AV_CODEC_ID_GREMLIN_DPCM;
105cabdff1aSopenharmony_ci        } else {
106cabdff1aSopenharmony_ci            ast->codecpar->codec_id = (snd_flags & 4) ? AV_CODEC_ID_PCM_S16LE : AV_CODEC_ID_PCM_U8;
107cabdff1aSopenharmony_ci        }
108cabdff1aSopenharmony_ci
109cabdff1aSopenharmony_ci        avpriv_set_pts_info(ast, 64, 1, ast->codecpar->sample_rate);
110cabdff1aSopenharmony_ci        gdv->audio_size = (ast->codecpar->sample_rate / fps) *
111cabdff1aSopenharmony_ci                           ast->codecpar->ch_layout.nb_channels *
112cabdff1aSopenharmony_ci                           (1 + !!(snd_flags & 4)) / (1 + !!(snd_flags & 8));
113cabdff1aSopenharmony_ci        gdv->is_audio = 1;
114cabdff1aSopenharmony_ci    } else {
115cabdff1aSopenharmony_ci        avio_skip(pb, 2);
116cabdff1aSopenharmony_ci    }
117cabdff1aSopenharmony_ci    vid_depth = avio_rl16(pb);
118cabdff1aSopenharmony_ci    avio_skip(pb, 4);
119cabdff1aSopenharmony_ci
120cabdff1aSopenharmony_ci    vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
121cabdff1aSopenharmony_ci    vst->codecpar->codec_id   = AV_CODEC_ID_GDV;
122cabdff1aSopenharmony_ci    vst->codecpar->codec_tag  = 0;
123cabdff1aSopenharmony_ci    vst->codecpar->width      = avio_rl16(pb);
124cabdff1aSopenharmony_ci    vst->codecpar->height     = avio_rl16(pb);
125cabdff1aSopenharmony_ci
126cabdff1aSopenharmony_ci    if (vst->codecpar->width == 0 || vst->codecpar->height == 0) {
127cabdff1aSopenharmony_ci        int i;
128cabdff1aSopenharmony_ci
129cabdff1aSopenharmony_ci        for (i = 0; i < FF_ARRAY_ELEMS(FixedSize) - 1; i++) {
130cabdff1aSopenharmony_ci            if (FixedSize[i].id == size_id)
131cabdff1aSopenharmony_ci                break;
132cabdff1aSopenharmony_ci        }
133cabdff1aSopenharmony_ci
134cabdff1aSopenharmony_ci        vst->codecpar->width  = FixedSize[i].width;
135cabdff1aSopenharmony_ci        vst->codecpar->height = FixedSize[i].height;
136cabdff1aSopenharmony_ci    }
137cabdff1aSopenharmony_ci
138cabdff1aSopenharmony_ci    avpriv_set_pts_info(vst, 64, 1, fps);
139cabdff1aSopenharmony_ci
140cabdff1aSopenharmony_ci    if (vid_depth & 1) {
141cabdff1aSopenharmony_ci        int i;
142cabdff1aSopenharmony_ci
143cabdff1aSopenharmony_ci        for (i = 0; i < 256; i++) {
144cabdff1aSopenharmony_ci            unsigned r = avio_r8(pb);
145cabdff1aSopenharmony_ci            unsigned g = avio_r8(pb);
146cabdff1aSopenharmony_ci            unsigned b = avio_r8(pb);
147cabdff1aSopenharmony_ci            gdv->pal[i] = 0xFFU << 24 | r << 18 | g << 10 | b << 2;
148cabdff1aSopenharmony_ci        }
149cabdff1aSopenharmony_ci    }
150cabdff1aSopenharmony_ci
151cabdff1aSopenharmony_ci    gdv->is_first_video = 1;
152cabdff1aSopenharmony_ci
153cabdff1aSopenharmony_ci    return 0;
154cabdff1aSopenharmony_ci}
155cabdff1aSopenharmony_ci
156cabdff1aSopenharmony_cistatic int gdv_read_packet(AVFormatContext *ctx, AVPacket *pkt)
157cabdff1aSopenharmony_ci{
158cabdff1aSopenharmony_ci    GDVContext *gdv = ctx->priv_data;
159cabdff1aSopenharmony_ci    AVIOContext *pb = ctx->pb;
160cabdff1aSopenharmony_ci    int ret;
161cabdff1aSopenharmony_ci
162cabdff1aSopenharmony_ci    if (avio_feof(pb))
163cabdff1aSopenharmony_ci        return pb->error ? pb->error : AVERROR_EOF;
164cabdff1aSopenharmony_ci
165cabdff1aSopenharmony_ci    if (gdv->audio_size && gdv->is_audio) {
166cabdff1aSopenharmony_ci        ret = av_get_packet(pb, pkt, gdv->audio_size);
167cabdff1aSopenharmony_ci        if (ret < 0)
168cabdff1aSopenharmony_ci            return ret;
169cabdff1aSopenharmony_ci        pkt->stream_index = 1;
170cabdff1aSopenharmony_ci        gdv->is_audio = 0;
171cabdff1aSopenharmony_ci    } else {
172cabdff1aSopenharmony_ci        uint8_t *pal;
173cabdff1aSopenharmony_ci
174cabdff1aSopenharmony_ci        if (avio_rl16(pb) != 0x1305)
175cabdff1aSopenharmony_ci            return AVERROR_INVALIDDATA;
176cabdff1aSopenharmony_ci        ret = av_get_packet(pb, pkt, 4 + avio_rl16(pb));
177cabdff1aSopenharmony_ci        if (ret < 0)
178cabdff1aSopenharmony_ci            return ret;
179cabdff1aSopenharmony_ci        pkt->stream_index = 0;
180cabdff1aSopenharmony_ci        gdv->is_audio = 1;
181cabdff1aSopenharmony_ci
182cabdff1aSopenharmony_ci        if (gdv->is_first_video) {
183cabdff1aSopenharmony_ci            pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE,
184cabdff1aSopenharmony_ci                                          AVPALETTE_SIZE);
185cabdff1aSopenharmony_ci            if (!pal) {
186cabdff1aSopenharmony_ci                return AVERROR(ENOMEM);
187cabdff1aSopenharmony_ci            }
188cabdff1aSopenharmony_ci            memcpy(pal, gdv->pal, AVPALETTE_SIZE);
189cabdff1aSopenharmony_ci            pkt->flags |= AV_PKT_FLAG_KEY;
190cabdff1aSopenharmony_ci            gdv->is_first_video = 0;
191cabdff1aSopenharmony_ci        }
192cabdff1aSopenharmony_ci    }
193cabdff1aSopenharmony_ci
194cabdff1aSopenharmony_ci    return 0;
195cabdff1aSopenharmony_ci}
196cabdff1aSopenharmony_ci
197cabdff1aSopenharmony_ciconst AVInputFormat ff_gdv_demuxer = {
198cabdff1aSopenharmony_ci    .name           = "gdv",
199cabdff1aSopenharmony_ci    .long_name      = NULL_IF_CONFIG_SMALL("Gremlin Digital Video"),
200cabdff1aSopenharmony_ci    .priv_data_size = sizeof(GDVContext),
201cabdff1aSopenharmony_ci    .read_probe     = gdv_read_probe,
202cabdff1aSopenharmony_ci    .read_header    = gdv_read_header,
203cabdff1aSopenharmony_ci    .read_packet    = gdv_read_packet,
204cabdff1aSopenharmony_ci};
205