1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Argonaut Games CVG (de)muxer 3cabdff1aSopenharmony_ci * 4cabdff1aSopenharmony_ci * Copyright (C) 2021 Zane van Iperen (zane@zanevaniperen.com) 5cabdff1aSopenharmony_ci * 6cabdff1aSopenharmony_ci * This file is part of FFmpeg. 7cabdff1aSopenharmony_ci * 8cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 9cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 10cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 11cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 12cabdff1aSopenharmony_ci * 13cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 14cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 15cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16cabdff1aSopenharmony_ci * Lesser General Public License for more details. 17cabdff1aSopenharmony_ci * 18cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 19cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 20cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21cabdff1aSopenharmony_ci */ 22cabdff1aSopenharmony_ci 23cabdff1aSopenharmony_ci#include "config_components.h" 24cabdff1aSopenharmony_ci 25cabdff1aSopenharmony_ci#include "libavutil/avstring.h" 26cabdff1aSopenharmony_ci#include "libavutil/channel_layout.h" 27cabdff1aSopenharmony_ci#include "avformat.h" 28cabdff1aSopenharmony_ci#include "internal.h" 29cabdff1aSopenharmony_ci#include "libavutil/opt.h" 30cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h" 31cabdff1aSopenharmony_ci 32cabdff1aSopenharmony_ci/* 33cabdff1aSopenharmony_ci * .CVG files are essentially PSX ADPCM wrapped with a size and checksum. 34cabdff1aSopenharmony_ci * Found in the PSX versions of the game. 35cabdff1aSopenharmony_ci */ 36cabdff1aSopenharmony_ci 37cabdff1aSopenharmony_ci#define ARGO_CVG_HEADER_SIZE 12 38cabdff1aSopenharmony_ci#define ARGO_CVG_BLOCK_ALIGN 0x10 39cabdff1aSopenharmony_ci#define ARGO_CVG_NB_BLOCKS 32 40cabdff1aSopenharmony_ci#define ARGO_CVG_SAMPLES_PER_BLOCK 28 41cabdff1aSopenharmony_ci 42cabdff1aSopenharmony_citypedef struct ArgoCVGHeader { 43cabdff1aSopenharmony_ci uint32_t size; /*< File size -8 (this + trailing checksum) */ 44cabdff1aSopenharmony_ci uint32_t unk1; /*< Unknown. Always seems to be 0 or 1. */ 45cabdff1aSopenharmony_ci uint32_t unk2; /*< Unknown. Always seems to be 0 or 1. */ 46cabdff1aSopenharmony_ci} ArgoCVGHeader; 47cabdff1aSopenharmony_ci 48cabdff1aSopenharmony_citypedef struct ArgoCVGOverride { 49cabdff1aSopenharmony_ci const char *name; 50cabdff1aSopenharmony_ci ArgoCVGHeader header; 51cabdff1aSopenharmony_ci uint32_t checksum; 52cabdff1aSopenharmony_ci int sample_rate; 53cabdff1aSopenharmony_ci} ArgoCVGOverride; 54cabdff1aSopenharmony_ci 55cabdff1aSopenharmony_citypedef struct ArgoCVGDemuxContext { 56cabdff1aSopenharmony_ci ArgoCVGHeader header; 57cabdff1aSopenharmony_ci uint32_t checksum; 58cabdff1aSopenharmony_ci uint32_t num_blocks; 59cabdff1aSopenharmony_ci uint32_t blocks_read; 60cabdff1aSopenharmony_ci} ArgoCVGDemuxContext; 61cabdff1aSopenharmony_ci 62cabdff1aSopenharmony_citypedef struct ArgoCVGMuxContext { 63cabdff1aSopenharmony_ci const AVClass *class; 64cabdff1aSopenharmony_ci int skip_rate_check; 65cabdff1aSopenharmony_ci uint32_t checksum; 66cabdff1aSopenharmony_ci size_t size; 67cabdff1aSopenharmony_ci} ArgoCVGMuxContext; 68cabdff1aSopenharmony_ci 69cabdff1aSopenharmony_ci#if CONFIG_ARGO_CVG_DEMUXER 70cabdff1aSopenharmony_ci/* "Special" files that are played at a different rate. */ 71cabdff1aSopenharmony_cistatic ArgoCVGOverride overrides[] = { 72cabdff1aSopenharmony_ci { "CRYS.CVG", { 23592, 0, 1 }, 2495499, 88200 }, /* Beta */ 73cabdff1aSopenharmony_ci { "REDCRY88.CVG", { 38280, 0, 1 }, 4134848, 88200 }, /* Beta */ 74cabdff1aSopenharmony_ci { "DANLOOP1.CVG", { 54744, 1, 0 }, 5684641, 37800 }, /* Beta */ 75cabdff1aSopenharmony_ci { "PICKUP88.CVG", { 12904, 0, 1 }, 1348091, 48000 }, /* Beta */ 76cabdff1aSopenharmony_ci { "SELECT1.CVG", { 5080, 0, 1 }, 549987, 44100 }, /* Beta */ 77cabdff1aSopenharmony_ci}; 78cabdff1aSopenharmony_ci 79cabdff1aSopenharmony_cistatic int argo_cvg_probe(const AVProbeData *p) 80cabdff1aSopenharmony_ci{ 81cabdff1aSopenharmony_ci ArgoCVGHeader cvg; 82cabdff1aSopenharmony_ci 83cabdff1aSopenharmony_ci /* 84cabdff1aSopenharmony_ci * It's almost impossible to detect these files based 85cabdff1aSopenharmony_ci * on the header alone. File extension is (unfortunately) 86cabdff1aSopenharmony_ci * the best way forward. 87cabdff1aSopenharmony_ci */ 88cabdff1aSopenharmony_ci if (!av_match_ext(p->filename, "cvg")) 89cabdff1aSopenharmony_ci return 0; 90cabdff1aSopenharmony_ci 91cabdff1aSopenharmony_ci if (p->buf_size < ARGO_CVG_HEADER_SIZE) 92cabdff1aSopenharmony_ci return 0; 93cabdff1aSopenharmony_ci 94cabdff1aSopenharmony_ci cvg.size = AV_RL32(p->buf + 0); 95cabdff1aSopenharmony_ci cvg.unk1 = AV_RL32(p->buf + 4); 96cabdff1aSopenharmony_ci cvg.unk2 = AV_RL32(p->buf + 8); 97cabdff1aSopenharmony_ci 98cabdff1aSopenharmony_ci if (cvg.size < 8) 99cabdff1aSopenharmony_ci return 0; 100cabdff1aSopenharmony_ci 101cabdff1aSopenharmony_ci if (cvg.unk1 != 0 && cvg.unk1 != 1) 102cabdff1aSopenharmony_ci return 0; 103cabdff1aSopenharmony_ci 104cabdff1aSopenharmony_ci if (cvg.unk2 != 0 && cvg.unk2 != 1) 105cabdff1aSopenharmony_ci return 0; 106cabdff1aSopenharmony_ci 107cabdff1aSopenharmony_ci return AVPROBE_SCORE_MAX / 4 + 1; 108cabdff1aSopenharmony_ci} 109cabdff1aSopenharmony_ci 110cabdff1aSopenharmony_cistatic int argo_cvg_read_checksum(AVIOContext *pb, const ArgoCVGHeader *cvg, uint32_t *checksum) 111cabdff1aSopenharmony_ci{ 112cabdff1aSopenharmony_ci int ret; 113cabdff1aSopenharmony_ci uint8_t buf[4]; 114cabdff1aSopenharmony_ci 115cabdff1aSopenharmony_ci if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) { 116cabdff1aSopenharmony_ci *checksum = 0; 117cabdff1aSopenharmony_ci return 0; 118cabdff1aSopenharmony_ci } 119cabdff1aSopenharmony_ci 120cabdff1aSopenharmony_ci if ((ret = avio_seek(pb, cvg->size + 4, SEEK_SET)) < 0) 121cabdff1aSopenharmony_ci return ret; 122cabdff1aSopenharmony_ci 123cabdff1aSopenharmony_ci /* NB: Not using avio_rl32() because no error checking. */ 124cabdff1aSopenharmony_ci if ((ret = avio_read(pb, buf, sizeof(buf))) < 0) 125cabdff1aSopenharmony_ci return ret; 126cabdff1aSopenharmony_ci else if (ret != sizeof(buf)) 127cabdff1aSopenharmony_ci return AVERROR(EIO); 128cabdff1aSopenharmony_ci 129cabdff1aSopenharmony_ci if ((ret = avio_seek(pb, ARGO_CVG_HEADER_SIZE, SEEK_SET)) < 0) 130cabdff1aSopenharmony_ci return ret; 131cabdff1aSopenharmony_ci 132cabdff1aSopenharmony_ci *checksum = AV_RL32(buf); 133cabdff1aSopenharmony_ci return 0; 134cabdff1aSopenharmony_ci} 135cabdff1aSopenharmony_ci 136cabdff1aSopenharmony_cistatic int argo_cvg_read_header(AVFormatContext *s) 137cabdff1aSopenharmony_ci{ 138cabdff1aSopenharmony_ci int ret; 139cabdff1aSopenharmony_ci AVStream *st; 140cabdff1aSopenharmony_ci AVCodecParameters *par; 141cabdff1aSopenharmony_ci uint8_t buf[ARGO_CVG_HEADER_SIZE]; 142cabdff1aSopenharmony_ci const char *filename = av_basename(s->url); 143cabdff1aSopenharmony_ci ArgoCVGDemuxContext *ctx = s->priv_data; 144cabdff1aSopenharmony_ci 145cabdff1aSopenharmony_ci if (!(st = avformat_new_stream(s, NULL))) 146cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 147cabdff1aSopenharmony_ci 148cabdff1aSopenharmony_ci if ((ret = avio_read(s->pb, buf, ARGO_CVG_HEADER_SIZE)) < 0) 149cabdff1aSopenharmony_ci return ret; 150cabdff1aSopenharmony_ci else if (ret != ARGO_CVG_HEADER_SIZE) 151cabdff1aSopenharmony_ci return AVERROR(EIO); 152cabdff1aSopenharmony_ci 153cabdff1aSopenharmony_ci ctx->header.size = AV_RL32(buf + 0); 154cabdff1aSopenharmony_ci ctx->header.unk1 = AV_RL32(buf + 4); 155cabdff1aSopenharmony_ci ctx->header.unk2 = AV_RL32(buf + 8); 156cabdff1aSopenharmony_ci 157cabdff1aSopenharmony_ci if (ctx->header.size < 8) 158cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 159cabdff1aSopenharmony_ci 160cabdff1aSopenharmony_ci av_log(s, AV_LOG_TRACE, "size = %u\n", ctx->header.size); 161cabdff1aSopenharmony_ci av_log(s, AV_LOG_TRACE, "unk = %u, %u\n", ctx->header.unk1, ctx->header.unk2); 162cabdff1aSopenharmony_ci 163cabdff1aSopenharmony_ci if ((ret = argo_cvg_read_checksum(s->pb, &ctx->header, &ctx->checksum)) < 0) 164cabdff1aSopenharmony_ci return ret; 165cabdff1aSopenharmony_ci 166cabdff1aSopenharmony_ci av_log(s, AV_LOG_TRACE, "checksum = %u\n", ctx->checksum); 167cabdff1aSopenharmony_ci 168cabdff1aSopenharmony_ci par = st->codecpar; 169cabdff1aSopenharmony_ci par->codec_type = AVMEDIA_TYPE_AUDIO; 170cabdff1aSopenharmony_ci par->codec_id = AV_CODEC_ID_ADPCM_PSX; 171cabdff1aSopenharmony_ci par->sample_rate = 22050; 172cabdff1aSopenharmony_ci 173cabdff1aSopenharmony_ci for (size_t i = 0; i < FF_ARRAY_ELEMS(overrides); i++) { 174cabdff1aSopenharmony_ci const ArgoCVGOverride *ovr = overrides + i; 175cabdff1aSopenharmony_ci if (ovr->header.size != ctx->header.size || 176cabdff1aSopenharmony_ci ovr->header.unk1 != ctx->header.unk1 || 177cabdff1aSopenharmony_ci ovr->header.unk2 != ctx->header.unk2 || 178cabdff1aSopenharmony_ci ovr->checksum != ctx->checksum || 179cabdff1aSopenharmony_ci av_strcasecmp(filename, ovr->name) != 0) 180cabdff1aSopenharmony_ci continue; 181cabdff1aSopenharmony_ci 182cabdff1aSopenharmony_ci av_log(s, AV_LOG_TRACE, "found override, name = %s\n", ovr->name); 183cabdff1aSopenharmony_ci par->sample_rate = ovr->sample_rate; 184cabdff1aSopenharmony_ci break; 185cabdff1aSopenharmony_ci } 186cabdff1aSopenharmony_ci 187cabdff1aSopenharmony_ci par->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; 188cabdff1aSopenharmony_ci 189cabdff1aSopenharmony_ci par->bits_per_coded_sample = 4; 190cabdff1aSopenharmony_ci par->block_align = ARGO_CVG_BLOCK_ALIGN; 191cabdff1aSopenharmony_ci par->bit_rate = par->sample_rate * par->bits_per_coded_sample; 192cabdff1aSopenharmony_ci 193cabdff1aSopenharmony_ci ctx->num_blocks = (ctx->header.size - 8) / ARGO_CVG_BLOCK_ALIGN; 194cabdff1aSopenharmony_ci 195cabdff1aSopenharmony_ci av_log(s, AV_LOG_TRACE, "num blocks = %u\n", ctx->num_blocks); 196cabdff1aSopenharmony_ci 197cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 64, 1, par->sample_rate); 198cabdff1aSopenharmony_ci 199cabdff1aSopenharmony_ci st->start_time = 0; 200cabdff1aSopenharmony_ci st->duration = ctx->num_blocks * ARGO_CVG_SAMPLES_PER_BLOCK; 201cabdff1aSopenharmony_ci st->nb_frames = ctx->num_blocks; 202cabdff1aSopenharmony_ci return 0; 203cabdff1aSopenharmony_ci} 204cabdff1aSopenharmony_ci 205cabdff1aSopenharmony_cistatic int argo_cvg_read_packet(AVFormatContext *s, AVPacket *pkt) 206cabdff1aSopenharmony_ci{ 207cabdff1aSopenharmony_ci int ret; 208cabdff1aSopenharmony_ci AVStream *st = s->streams[0]; 209cabdff1aSopenharmony_ci ArgoCVGDemuxContext *ctx = s->priv_data; 210cabdff1aSopenharmony_ci 211cabdff1aSopenharmony_ci if (ctx->blocks_read >= ctx->num_blocks) 212cabdff1aSopenharmony_ci return AVERROR_EOF; 213cabdff1aSopenharmony_ci 214cabdff1aSopenharmony_ci ret = av_get_packet(s->pb, pkt, st->codecpar->block_align * 215cabdff1aSopenharmony_ci FFMIN(ARGO_CVG_NB_BLOCKS, ctx->num_blocks - ctx->blocks_read)); 216cabdff1aSopenharmony_ci 217cabdff1aSopenharmony_ci if (ret < 0) 218cabdff1aSopenharmony_ci return ret; 219cabdff1aSopenharmony_ci 220cabdff1aSopenharmony_ci if (ret % st->codecpar->block_align != 0) 221cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 222cabdff1aSopenharmony_ci 223cabdff1aSopenharmony_ci pkt->stream_index = 0; 224cabdff1aSopenharmony_ci pkt->duration = ARGO_CVG_SAMPLES_PER_BLOCK * (ret / st->codecpar->block_align); 225cabdff1aSopenharmony_ci pkt->pts = ctx->blocks_read * ARGO_CVG_SAMPLES_PER_BLOCK; 226cabdff1aSopenharmony_ci pkt->flags &= ~AV_PKT_FLAG_CORRUPT; 227cabdff1aSopenharmony_ci 228cabdff1aSopenharmony_ci ctx->blocks_read += ret / st->codecpar->block_align; 229cabdff1aSopenharmony_ci 230cabdff1aSopenharmony_ci return 0; 231cabdff1aSopenharmony_ci} 232cabdff1aSopenharmony_ci 233cabdff1aSopenharmony_cistatic int argo_cvg_seek(AVFormatContext *s, int stream_index, 234cabdff1aSopenharmony_ci int64_t pts, int flags) 235cabdff1aSopenharmony_ci{ 236cabdff1aSopenharmony_ci int64_t ret; 237cabdff1aSopenharmony_ci ArgoCVGDemuxContext *ctx = s->priv_data; 238cabdff1aSopenharmony_ci 239cabdff1aSopenharmony_ci if (pts != 0 || stream_index != 0) 240cabdff1aSopenharmony_ci return AVERROR(EINVAL); 241cabdff1aSopenharmony_ci 242cabdff1aSopenharmony_ci if ((ret = avio_seek(s->pb, ARGO_CVG_HEADER_SIZE, SEEK_SET)) < 0) 243cabdff1aSopenharmony_ci return ret; 244cabdff1aSopenharmony_ci 245cabdff1aSopenharmony_ci ctx->blocks_read = 0; 246cabdff1aSopenharmony_ci return 0; 247cabdff1aSopenharmony_ci} 248cabdff1aSopenharmony_ci 249cabdff1aSopenharmony_ciconst AVInputFormat ff_argo_cvg_demuxer = { 250cabdff1aSopenharmony_ci .name = "argo_cvg", 251cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games CVG"), 252cabdff1aSopenharmony_ci .priv_data_size = sizeof(ArgoCVGDemuxContext), 253cabdff1aSopenharmony_ci .read_probe = argo_cvg_probe, 254cabdff1aSopenharmony_ci .read_header = argo_cvg_read_header, 255cabdff1aSopenharmony_ci .read_packet = argo_cvg_read_packet, 256cabdff1aSopenharmony_ci .read_seek = argo_cvg_seek, 257cabdff1aSopenharmony_ci}; 258cabdff1aSopenharmony_ci#endif 259cabdff1aSopenharmony_ci 260cabdff1aSopenharmony_ci#if CONFIG_ARGO_CVG_MUXER 261cabdff1aSopenharmony_cistatic int argo_cvg_write_init(AVFormatContext *s) 262cabdff1aSopenharmony_ci{ 263cabdff1aSopenharmony_ci ArgoCVGMuxContext *ctx = s->priv_data; 264cabdff1aSopenharmony_ci const AVCodecParameters *par; 265cabdff1aSopenharmony_ci 266cabdff1aSopenharmony_ci if (s->nb_streams != 1) { 267cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "CVG files have exactly one stream\n"); 268cabdff1aSopenharmony_ci return AVERROR(EINVAL); 269cabdff1aSopenharmony_ci } 270cabdff1aSopenharmony_ci 271cabdff1aSopenharmony_ci par = s->streams[0]->codecpar; 272cabdff1aSopenharmony_ci 273cabdff1aSopenharmony_ci if (par->codec_id != AV_CODEC_ID_ADPCM_PSX) { 274cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "%s codec not supported\n", 275cabdff1aSopenharmony_ci avcodec_get_name(par->codec_id)); 276cabdff1aSopenharmony_ci return AVERROR(EINVAL); 277cabdff1aSopenharmony_ci } 278cabdff1aSopenharmony_ci 279cabdff1aSopenharmony_ci if (par->ch_layout.nb_channels != 1) { 280cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "CVG files only support 1 channel\n"); 281cabdff1aSopenharmony_ci return AVERROR(EINVAL); 282cabdff1aSopenharmony_ci } 283cabdff1aSopenharmony_ci 284cabdff1aSopenharmony_ci if (par->block_align != ARGO_CVG_BLOCK_ALIGN) 285cabdff1aSopenharmony_ci return AVERROR(EINVAL); 286cabdff1aSopenharmony_ci 287cabdff1aSopenharmony_ci if (!ctx->skip_rate_check && par->sample_rate != 22050) { 288cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Sample rate must be 22050\n"); 289cabdff1aSopenharmony_ci return AVERROR(EINVAL); 290cabdff1aSopenharmony_ci } 291cabdff1aSopenharmony_ci 292cabdff1aSopenharmony_ci if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) { 293cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Stream not seekable, unable to write output file\n"); 294cabdff1aSopenharmony_ci return AVERROR(EINVAL); 295cabdff1aSopenharmony_ci } 296cabdff1aSopenharmony_ci 297cabdff1aSopenharmony_ci return 0; 298cabdff1aSopenharmony_ci} 299cabdff1aSopenharmony_ci 300cabdff1aSopenharmony_cistatic int argo_cvg_write_header(AVFormatContext *s) 301cabdff1aSopenharmony_ci{ 302cabdff1aSopenharmony_ci ArgoCVGMuxContext *ctx = s->priv_data; 303cabdff1aSopenharmony_ci 304cabdff1aSopenharmony_ci avio_wl32(s->pb, 0); /* Size, fixed later. */ 305cabdff1aSopenharmony_ci avio_wl32(s->pb, 0); 306cabdff1aSopenharmony_ci avio_wl32(s->pb, 1); 307cabdff1aSopenharmony_ci 308cabdff1aSopenharmony_ci ctx->checksum = 1; 309cabdff1aSopenharmony_ci ctx->size = 8; 310cabdff1aSopenharmony_ci return 0; 311cabdff1aSopenharmony_ci} 312cabdff1aSopenharmony_ci 313cabdff1aSopenharmony_cistatic int argo_cvg_write_packet(AVFormatContext *s, AVPacket *pkt) 314cabdff1aSopenharmony_ci{ 315cabdff1aSopenharmony_ci ArgoCVGMuxContext *ctx = s->priv_data; 316cabdff1aSopenharmony_ci AVCodecParameters *par = s->streams[0]->codecpar; 317cabdff1aSopenharmony_ci 318cabdff1aSopenharmony_ci if (pkt->size % par->block_align != 0) 319cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 320cabdff1aSopenharmony_ci 321cabdff1aSopenharmony_ci avio_write(s->pb, pkt->data, pkt->size); 322cabdff1aSopenharmony_ci 323cabdff1aSopenharmony_ci ctx->size += pkt->size; 324cabdff1aSopenharmony_ci 325cabdff1aSopenharmony_ci if (ctx->size > UINT32_MAX) 326cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 327cabdff1aSopenharmony_ci 328cabdff1aSopenharmony_ci for (int i = 0; i < pkt->size; i++) 329cabdff1aSopenharmony_ci ctx->checksum += pkt->data[i]; 330cabdff1aSopenharmony_ci 331cabdff1aSopenharmony_ci return 0; 332cabdff1aSopenharmony_ci} 333cabdff1aSopenharmony_ci 334cabdff1aSopenharmony_cistatic int argo_cvg_write_trailer(AVFormatContext *s) 335cabdff1aSopenharmony_ci{ 336cabdff1aSopenharmony_ci ArgoCVGMuxContext *ctx = s->priv_data; 337cabdff1aSopenharmony_ci int64_t ret; 338cabdff1aSopenharmony_ci 339cabdff1aSopenharmony_ci ctx->checksum += (ctx->size & 255) 340cabdff1aSopenharmony_ci + ((ctx->size>> 8) & 255) 341cabdff1aSopenharmony_ci + ((ctx->size>>16) & 255) 342cabdff1aSopenharmony_ci + (ctx->size>>24); 343cabdff1aSopenharmony_ci 344cabdff1aSopenharmony_ci av_log(s, AV_LOG_TRACE, "size = %zu\n", ctx->size); 345cabdff1aSopenharmony_ci av_log(s, AV_LOG_TRACE, "checksum = %u\n", ctx->checksum); 346cabdff1aSopenharmony_ci 347cabdff1aSopenharmony_ci avio_wl32(s->pb, ctx->checksum); 348cabdff1aSopenharmony_ci 349cabdff1aSopenharmony_ci if ((ret = avio_seek(s->pb, 0, SEEK_SET)) < 0) 350cabdff1aSopenharmony_ci return ret; 351cabdff1aSopenharmony_ci 352cabdff1aSopenharmony_ci avio_wl32(s->pb, (uint32_t)ctx->size); 353cabdff1aSopenharmony_ci return 0; 354cabdff1aSopenharmony_ci} 355cabdff1aSopenharmony_ci 356cabdff1aSopenharmony_cistatic const AVOption argo_cvg_options[] = { 357cabdff1aSopenharmony_ci { 358cabdff1aSopenharmony_ci .name = "skip_rate_check", 359cabdff1aSopenharmony_ci .help = "skip sample rate check", 360cabdff1aSopenharmony_ci .offset = offsetof(ArgoCVGMuxContext, skip_rate_check), 361cabdff1aSopenharmony_ci .type = AV_OPT_TYPE_BOOL, 362cabdff1aSopenharmony_ci .default_val = {.i64 = 0}, 363cabdff1aSopenharmony_ci .min = 0, 364cabdff1aSopenharmony_ci .max = 1, 365cabdff1aSopenharmony_ci .flags = AV_OPT_FLAG_ENCODING_PARAM 366cabdff1aSopenharmony_ci }, 367cabdff1aSopenharmony_ci { NULL } 368cabdff1aSopenharmony_ci}; 369cabdff1aSopenharmony_ci 370cabdff1aSopenharmony_cistatic const AVClass argo_cvg_muxer_class = { 371cabdff1aSopenharmony_ci .class_name = "argo_cvg_muxer", 372cabdff1aSopenharmony_ci .item_name = av_default_item_name, 373cabdff1aSopenharmony_ci .option = argo_cvg_options, 374cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT 375cabdff1aSopenharmony_ci}; 376cabdff1aSopenharmony_ci 377cabdff1aSopenharmony_ciconst AVOutputFormat ff_argo_cvg_muxer = { 378cabdff1aSopenharmony_ci .name = "argo_cvg", 379cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games CVG"), 380cabdff1aSopenharmony_ci .extensions = "cvg", 381cabdff1aSopenharmony_ci .audio_codec = AV_CODEC_ID_ADPCM_PSX, 382cabdff1aSopenharmony_ci .video_codec = AV_CODEC_ID_NONE, 383cabdff1aSopenharmony_ci .init = argo_cvg_write_init, 384cabdff1aSopenharmony_ci .write_header = argo_cvg_write_header, 385cabdff1aSopenharmony_ci .write_packet = argo_cvg_write_packet, 386cabdff1aSopenharmony_ci .write_trailer = argo_cvg_write_trailer, 387cabdff1aSopenharmony_ci .priv_class = &argo_cvg_muxer_class, 388cabdff1aSopenharmony_ci .priv_data_size = sizeof(ArgoCVGMuxContext), 389cabdff1aSopenharmony_ci}; 390cabdff1aSopenharmony_ci#endif 391