1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Westwood Studios VQA Format Demuxer 3cabdff1aSopenharmony_ci * Copyright (c) 2003 Mike Melanson <melanson@pcisys.net> 4cabdff1aSopenharmony_ci * Copyright (c) 2021 Pekka Väänänen <pekka.vaananen@iki.fi> 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/** 24cabdff1aSopenharmony_ci * @file 25cabdff1aSopenharmony_ci * Westwood Studios VQA file demuxer 26cabdff1aSopenharmony_ci * by Mike Melanson (melanson@pcisys.net) 27cabdff1aSopenharmony_ci * for more information on the Westwood file formats, visit: 28cabdff1aSopenharmony_ci * http://www.pcisys.net/~melanson/codecs/ 29cabdff1aSopenharmony_ci * http://www.geocities.com/SiliconValley/8682/aud3.txt 30cabdff1aSopenharmony_ci */ 31cabdff1aSopenharmony_ci 32cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h" 33cabdff1aSopenharmony_ci#include "avformat.h" 34cabdff1aSopenharmony_ci#include "avio_internal.h" 35cabdff1aSopenharmony_ci#include "demux.h" 36cabdff1aSopenharmony_ci#include "internal.h" 37cabdff1aSopenharmony_ci 38cabdff1aSopenharmony_ci#define FORM_TAG MKBETAG('F', 'O', 'R', 'M') 39cabdff1aSopenharmony_ci#define WVQA_TAG MKBETAG('W', 'V', 'Q', 'A') 40cabdff1aSopenharmony_ci#define VQHD_TAG MKBETAG('V', 'Q', 'H', 'D') 41cabdff1aSopenharmony_ci#define FINF_TAG MKBETAG('F', 'I', 'N', 'F') 42cabdff1aSopenharmony_ci#define SND0_TAG MKBETAG('S', 'N', 'D', '0') 43cabdff1aSopenharmony_ci#define SND1_TAG MKBETAG('S', 'N', 'D', '1') 44cabdff1aSopenharmony_ci#define SND2_TAG MKBETAG('S', 'N', 'D', '2') 45cabdff1aSopenharmony_ci#define VQFR_TAG MKBETAG('V', 'Q', 'F', 'R') 46cabdff1aSopenharmony_ci#define VQFL_TAG MKBETAG('V', 'Q', 'F', 'L') 47cabdff1aSopenharmony_ci 48cabdff1aSopenharmony_ci/* don't know what these tags are for, but acknowledge their existence */ 49cabdff1aSopenharmony_ci#define CINF_TAG MKBETAG('C', 'I', 'N', 'F') 50cabdff1aSopenharmony_ci#define CINH_TAG MKBETAG('C', 'I', 'N', 'H') 51cabdff1aSopenharmony_ci#define CIND_TAG MKBETAG('C', 'I', 'N', 'D') 52cabdff1aSopenharmony_ci#define LINF_TAG MKBETAG('L', 'I', 'N', 'F') 53cabdff1aSopenharmony_ci#define PINF_TAG MKBETAG('P', 'I', 'N', 'F') 54cabdff1aSopenharmony_ci#define PINH_TAG MKBETAG('P', 'I', 'N', 'H') 55cabdff1aSopenharmony_ci#define PIND_TAG MKBETAG('P', 'I', 'N', 'D') 56cabdff1aSopenharmony_ci#define CMDS_TAG MKBETAG('C', 'M', 'D', 'S') 57cabdff1aSopenharmony_ci#define SN2J_TAG MKBETAG('S', 'N', '2', 'J') 58cabdff1aSopenharmony_ci#define VIEW_TAG MKBETAG('V', 'I', 'E', 'W') 59cabdff1aSopenharmony_ci#define ZBUF_TAG MKBETAG('Z', 'B', 'U', 'F') 60cabdff1aSopenharmony_ci 61cabdff1aSopenharmony_ci#define VQA_HEADER_SIZE 0x2A 62cabdff1aSopenharmony_ci#define VQA_PREAMBLE_SIZE 8 63cabdff1aSopenharmony_ci 64cabdff1aSopenharmony_citypedef struct WsVqaDemuxContext { 65cabdff1aSopenharmony_ci int version; 66cabdff1aSopenharmony_ci int bps; 67cabdff1aSopenharmony_ci int channels; 68cabdff1aSopenharmony_ci int sample_rate; 69cabdff1aSopenharmony_ci int audio_stream_index; 70cabdff1aSopenharmony_ci int video_stream_index; 71cabdff1aSopenharmony_ci int64_t vqfl_chunk_pos; 72cabdff1aSopenharmony_ci int vqfl_chunk_size; 73cabdff1aSopenharmony_ci} WsVqaDemuxContext; 74cabdff1aSopenharmony_ci 75cabdff1aSopenharmony_cistatic int wsvqa_probe(const AVProbeData *p) 76cabdff1aSopenharmony_ci{ 77cabdff1aSopenharmony_ci /* need 12 bytes to qualify */ 78cabdff1aSopenharmony_ci if (p->buf_size < 12) 79cabdff1aSopenharmony_ci return 0; 80cabdff1aSopenharmony_ci 81cabdff1aSopenharmony_ci /* check for the VQA signatures */ 82cabdff1aSopenharmony_ci if ((AV_RB32(&p->buf[0]) != FORM_TAG) || 83cabdff1aSopenharmony_ci (AV_RB32(&p->buf[8]) != WVQA_TAG)) 84cabdff1aSopenharmony_ci return 0; 85cabdff1aSopenharmony_ci 86cabdff1aSopenharmony_ci return AVPROBE_SCORE_MAX; 87cabdff1aSopenharmony_ci} 88cabdff1aSopenharmony_ci 89cabdff1aSopenharmony_cistatic int wsvqa_read_header(AVFormatContext *s) 90cabdff1aSopenharmony_ci{ 91cabdff1aSopenharmony_ci WsVqaDemuxContext *wsvqa = s->priv_data; 92cabdff1aSopenharmony_ci AVIOContext *pb = s->pb; 93cabdff1aSopenharmony_ci AVStream *st; 94cabdff1aSopenharmony_ci uint8_t *header; 95cabdff1aSopenharmony_ci uint8_t scratch[VQA_PREAMBLE_SIZE]; 96cabdff1aSopenharmony_ci uint32_t chunk_tag; 97cabdff1aSopenharmony_ci uint32_t chunk_size; 98cabdff1aSopenharmony_ci int fps, ret; 99cabdff1aSopenharmony_ci 100cabdff1aSopenharmony_ci /* initialize the video decoder stream */ 101cabdff1aSopenharmony_ci st = avformat_new_stream(s, NULL); 102cabdff1aSopenharmony_ci if (!st) 103cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 104cabdff1aSopenharmony_ci st->start_time = 0; 105cabdff1aSopenharmony_ci wsvqa->video_stream_index = st->index; 106cabdff1aSopenharmony_ci st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; 107cabdff1aSopenharmony_ci st->codecpar->codec_id = AV_CODEC_ID_WS_VQA; 108cabdff1aSopenharmony_ci st->codecpar->codec_tag = 0; /* no fourcc */ 109cabdff1aSopenharmony_ci 110cabdff1aSopenharmony_ci /* skip to the start of the VQA header */ 111cabdff1aSopenharmony_ci avio_seek(pb, 20, SEEK_SET); 112cabdff1aSopenharmony_ci 113cabdff1aSopenharmony_ci /* the VQA header needs to go to the decoder */ 114cabdff1aSopenharmony_ci if ((ret = ff_get_extradata(s, st->codecpar, pb, VQA_HEADER_SIZE)) < 0) 115cabdff1aSopenharmony_ci return ret; 116cabdff1aSopenharmony_ci header = st->codecpar->extradata; 117cabdff1aSopenharmony_ci st->codecpar->width = AV_RL16(&header[6]); 118cabdff1aSopenharmony_ci st->codecpar->height = AV_RL16(&header[8]); 119cabdff1aSopenharmony_ci fps = header[12]; 120cabdff1aSopenharmony_ci st->nb_frames = 121cabdff1aSopenharmony_ci st->duration = AV_RL16(&header[4]); 122cabdff1aSopenharmony_ci if (fps < 1 || fps > 30) { 123cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "invalid fps: %d\n", fps); 124cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 125cabdff1aSopenharmony_ci } 126cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 64, 1, fps); 127cabdff1aSopenharmony_ci 128cabdff1aSopenharmony_ci wsvqa->version = AV_RL16(&header[ 0]); 129cabdff1aSopenharmony_ci wsvqa->sample_rate = AV_RL16(&header[24]); 130cabdff1aSopenharmony_ci wsvqa->channels = header[26]; 131cabdff1aSopenharmony_ci wsvqa->bps = header[27]; 132cabdff1aSopenharmony_ci wsvqa->audio_stream_index = -1; 133cabdff1aSopenharmony_ci wsvqa->vqfl_chunk_pos = 0; 134cabdff1aSopenharmony_ci wsvqa->vqfl_chunk_size = 0; 135cabdff1aSopenharmony_ci 136cabdff1aSopenharmony_ci s->ctx_flags |= AVFMTCTX_NOHEADER; 137cabdff1aSopenharmony_ci 138cabdff1aSopenharmony_ci /* there are 0 or more chunks before the FINF chunk; iterate until 139cabdff1aSopenharmony_ci * FINF has been skipped and the file will be ready to be demuxed */ 140cabdff1aSopenharmony_ci do { 141cabdff1aSopenharmony_ci if (avio_read(pb, scratch, VQA_PREAMBLE_SIZE) != VQA_PREAMBLE_SIZE) 142cabdff1aSopenharmony_ci return AVERROR(EIO); 143cabdff1aSopenharmony_ci chunk_tag = AV_RB32(&scratch[0]); 144cabdff1aSopenharmony_ci chunk_size = AV_RB32(&scratch[4]); 145cabdff1aSopenharmony_ci 146cabdff1aSopenharmony_ci /* catch any unknown header tags, for curiosity */ 147cabdff1aSopenharmony_ci switch (chunk_tag) { 148cabdff1aSopenharmony_ci case CINF_TAG: 149cabdff1aSopenharmony_ci case CINH_TAG: 150cabdff1aSopenharmony_ci case CIND_TAG: 151cabdff1aSopenharmony_ci case LINF_TAG: 152cabdff1aSopenharmony_ci case PINF_TAG: 153cabdff1aSopenharmony_ci case PINH_TAG: 154cabdff1aSopenharmony_ci case PIND_TAG: 155cabdff1aSopenharmony_ci case FINF_TAG: 156cabdff1aSopenharmony_ci case CMDS_TAG: 157cabdff1aSopenharmony_ci case VIEW_TAG: 158cabdff1aSopenharmony_ci case ZBUF_TAG: 159cabdff1aSopenharmony_ci break; 160cabdff1aSopenharmony_ci 161cabdff1aSopenharmony_ci default: 162cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, " note: unknown chunk seen (%s)\n", 163cabdff1aSopenharmony_ci av_fourcc2str(chunk_tag)); 164cabdff1aSopenharmony_ci break; 165cabdff1aSopenharmony_ci } 166cabdff1aSopenharmony_ci 167cabdff1aSopenharmony_ci avio_skip(pb, chunk_size); 168cabdff1aSopenharmony_ci } while (chunk_tag != FINF_TAG); 169cabdff1aSopenharmony_ci 170cabdff1aSopenharmony_ci return 0; 171cabdff1aSopenharmony_ci} 172cabdff1aSopenharmony_ci 173cabdff1aSopenharmony_cistatic int wsvqa_read_packet(AVFormatContext *s, 174cabdff1aSopenharmony_ci AVPacket *pkt) 175cabdff1aSopenharmony_ci{ 176cabdff1aSopenharmony_ci WsVqaDemuxContext *wsvqa = s->priv_data; 177cabdff1aSopenharmony_ci AVIOContext *pb = s->pb; 178cabdff1aSopenharmony_ci int ret = -1; 179cabdff1aSopenharmony_ci uint8_t preamble[VQA_PREAMBLE_SIZE]; 180cabdff1aSopenharmony_ci uint32_t chunk_type; 181cabdff1aSopenharmony_ci int chunk_size; 182cabdff1aSopenharmony_ci unsigned skip_byte; 183cabdff1aSopenharmony_ci 184cabdff1aSopenharmony_ci while (avio_read(pb, preamble, VQA_PREAMBLE_SIZE) == VQA_PREAMBLE_SIZE) { 185cabdff1aSopenharmony_ci chunk_type = AV_RB32(&preamble[0]); 186cabdff1aSopenharmony_ci chunk_size = AV_RB32(&preamble[4]); 187cabdff1aSopenharmony_ci 188cabdff1aSopenharmony_ci if (chunk_size < 0) 189cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 190cabdff1aSopenharmony_ci skip_byte = chunk_size & 0x01; 191cabdff1aSopenharmony_ci 192cabdff1aSopenharmony_ci if (chunk_type == VQFL_TAG) { 193cabdff1aSopenharmony_ci /* Each VQFL chunk carries only a codebook update inside which must be applied 194cabdff1aSopenharmony_ci * before the next VQFR is rendered. That's why we stash the VQFL offset here 195cabdff1aSopenharmony_ci * so it can be combined with the next VQFR packet. This way each packet 196cabdff1aSopenharmony_ci * includes a whole frame as expected. */ 197cabdff1aSopenharmony_ci wsvqa->vqfl_chunk_pos = avio_tell(pb); 198cabdff1aSopenharmony_ci if (chunk_size > 3 * (1 << 20)) 199cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 200cabdff1aSopenharmony_ci wsvqa->vqfl_chunk_size = chunk_size; 201cabdff1aSopenharmony_ci /* We need a big seekback buffer because there can be SNxx, VIEW and ZBUF 202cabdff1aSopenharmony_ci * chunks (<512 KiB total) in the stream before we read VQFR (<256 KiB) and 203cabdff1aSopenharmony_ci * seek back here. */ 204cabdff1aSopenharmony_ci ffio_ensure_seekback(pb, wsvqa->vqfl_chunk_size + (512 + 256) * 1024); 205cabdff1aSopenharmony_ci avio_skip(pb, chunk_size + skip_byte); 206cabdff1aSopenharmony_ci continue; 207cabdff1aSopenharmony_ci } else if ((chunk_type == SND0_TAG) || (chunk_type == SND1_TAG) || 208cabdff1aSopenharmony_ci (chunk_type == SND2_TAG) || (chunk_type == VQFR_TAG)) { 209cabdff1aSopenharmony_ci 210cabdff1aSopenharmony_ci ret= av_get_packet(pb, pkt, chunk_size); 211cabdff1aSopenharmony_ci if (ret<0) 212cabdff1aSopenharmony_ci return AVERROR(EIO); 213cabdff1aSopenharmony_ci 214cabdff1aSopenharmony_ci switch (chunk_type) { 215cabdff1aSopenharmony_ci case SND0_TAG: 216cabdff1aSopenharmony_ci case SND1_TAG: 217cabdff1aSopenharmony_ci case SND2_TAG: 218cabdff1aSopenharmony_ci if (wsvqa->audio_stream_index == -1) { 219cabdff1aSopenharmony_ci AVStream *st = avformat_new_stream(s, NULL); 220cabdff1aSopenharmony_ci if (!st) 221cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 222cabdff1aSopenharmony_ci 223cabdff1aSopenharmony_ci wsvqa->audio_stream_index = st->index; 224cabdff1aSopenharmony_ci if (!wsvqa->sample_rate) 225cabdff1aSopenharmony_ci wsvqa->sample_rate = 22050; 226cabdff1aSopenharmony_ci if (!wsvqa->channels) 227cabdff1aSopenharmony_ci wsvqa->channels = 1; 228cabdff1aSopenharmony_ci if (!wsvqa->bps) 229cabdff1aSopenharmony_ci wsvqa->bps = 8; 230cabdff1aSopenharmony_ci st->codecpar->sample_rate = wsvqa->sample_rate; 231cabdff1aSopenharmony_ci st->codecpar->bits_per_coded_sample = wsvqa->bps; 232cabdff1aSopenharmony_ci av_channel_layout_default(&st->codecpar->ch_layout, wsvqa->channels); 233cabdff1aSopenharmony_ci st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; 234cabdff1aSopenharmony_ci 235cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); 236cabdff1aSopenharmony_ci 237cabdff1aSopenharmony_ci switch (chunk_type) { 238cabdff1aSopenharmony_ci case SND0_TAG: 239cabdff1aSopenharmony_ci if (wsvqa->bps == 16) 240cabdff1aSopenharmony_ci st->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; 241cabdff1aSopenharmony_ci else 242cabdff1aSopenharmony_ci st->codecpar->codec_id = AV_CODEC_ID_PCM_U8; 243cabdff1aSopenharmony_ci break; 244cabdff1aSopenharmony_ci case SND1_TAG: 245cabdff1aSopenharmony_ci st->codecpar->codec_id = AV_CODEC_ID_WESTWOOD_SND1; 246cabdff1aSopenharmony_ci break; 247cabdff1aSopenharmony_ci case SND2_TAG: 248cabdff1aSopenharmony_ci st->codecpar->codec_id = AV_CODEC_ID_ADPCM_IMA_WS; 249cabdff1aSopenharmony_ci if ((ret = ff_alloc_extradata(st->codecpar, 2)) < 0) 250cabdff1aSopenharmony_ci return ret; 251cabdff1aSopenharmony_ci AV_WL16(st->codecpar->extradata, wsvqa->version); 252cabdff1aSopenharmony_ci break; 253cabdff1aSopenharmony_ci } 254cabdff1aSopenharmony_ci } 255cabdff1aSopenharmony_ci 256cabdff1aSopenharmony_ci pkt->stream_index = wsvqa->audio_stream_index; 257cabdff1aSopenharmony_ci switch (chunk_type) { 258cabdff1aSopenharmony_ci case SND1_TAG: 259cabdff1aSopenharmony_ci /* unpacked size is stored in header */ 260cabdff1aSopenharmony_ci if(pkt->data) 261cabdff1aSopenharmony_ci pkt->duration = AV_RL16(pkt->data) / wsvqa->channels; 262cabdff1aSopenharmony_ci break; 263cabdff1aSopenharmony_ci case SND2_TAG: 264cabdff1aSopenharmony_ci /* 2 samples/byte, 1 or 2 samples per frame depending on stereo */ 265cabdff1aSopenharmony_ci pkt->duration = (chunk_size * 2) / wsvqa->channels; 266cabdff1aSopenharmony_ci break; 267cabdff1aSopenharmony_ci } 268cabdff1aSopenharmony_ci break; 269cabdff1aSopenharmony_ci case VQFR_TAG: 270cabdff1aSopenharmony_ci /* if a new codebook is available inside an earlier a VQFL chunk then 271cabdff1aSopenharmony_ci * append it to 'pkt' */ 272cabdff1aSopenharmony_ci if (wsvqa->vqfl_chunk_size > 0) { 273cabdff1aSopenharmony_ci int64_t current_pos = pkt->pos; 274cabdff1aSopenharmony_ci 275cabdff1aSopenharmony_ci if (avio_seek(pb, wsvqa->vqfl_chunk_pos, SEEK_SET) < 0) 276cabdff1aSopenharmony_ci return AVERROR(EIO); 277cabdff1aSopenharmony_ci 278cabdff1aSopenharmony_ci /* the decoder expects chunks to be 16-bit aligned */ 279cabdff1aSopenharmony_ci if (wsvqa->vqfl_chunk_size % 2 == 1) 280cabdff1aSopenharmony_ci wsvqa->vqfl_chunk_size++; 281cabdff1aSopenharmony_ci 282cabdff1aSopenharmony_ci if (av_append_packet(pb, pkt, wsvqa->vqfl_chunk_size) < 0) 283cabdff1aSopenharmony_ci return AVERROR(EIO); 284cabdff1aSopenharmony_ci 285cabdff1aSopenharmony_ci if (avio_seek(pb, current_pos, SEEK_SET) < 0) 286cabdff1aSopenharmony_ci return AVERROR(EIO); 287cabdff1aSopenharmony_ci 288cabdff1aSopenharmony_ci wsvqa->vqfl_chunk_pos = 0; 289cabdff1aSopenharmony_ci wsvqa->vqfl_chunk_size = 0; 290cabdff1aSopenharmony_ci } 291cabdff1aSopenharmony_ci 292cabdff1aSopenharmony_ci pkt->stream_index = wsvqa->video_stream_index; 293cabdff1aSopenharmony_ci pkt->duration = 1; 294cabdff1aSopenharmony_ci break; 295cabdff1aSopenharmony_ci } 296cabdff1aSopenharmony_ci 297cabdff1aSopenharmony_ci /* stay on 16-bit alignment */ 298cabdff1aSopenharmony_ci if (skip_byte) 299cabdff1aSopenharmony_ci avio_skip(pb, 1); 300cabdff1aSopenharmony_ci 301cabdff1aSopenharmony_ci return ret; 302cabdff1aSopenharmony_ci } else { 303cabdff1aSopenharmony_ci switch(chunk_type){ 304cabdff1aSopenharmony_ci case CMDS_TAG: 305cabdff1aSopenharmony_ci case SN2J_TAG: 306cabdff1aSopenharmony_ci case VIEW_TAG: 307cabdff1aSopenharmony_ci case ZBUF_TAG: 308cabdff1aSopenharmony_ci break; 309cabdff1aSopenharmony_ci default: 310cabdff1aSopenharmony_ci av_log(s, AV_LOG_INFO, "Skipping unknown chunk %s\n", 311cabdff1aSopenharmony_ci av_fourcc2str(av_bswap32(chunk_type))); 312cabdff1aSopenharmony_ci } 313cabdff1aSopenharmony_ci avio_skip(pb, chunk_size + skip_byte); 314cabdff1aSopenharmony_ci } 315cabdff1aSopenharmony_ci } 316cabdff1aSopenharmony_ci 317cabdff1aSopenharmony_ci return ret; 318cabdff1aSopenharmony_ci} 319cabdff1aSopenharmony_ci 320cabdff1aSopenharmony_ciconst AVInputFormat ff_wsvqa_demuxer = { 321cabdff1aSopenharmony_ci .name = "wsvqa", 322cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Westwood Studios VQA"), 323cabdff1aSopenharmony_ci .priv_data_size = sizeof(WsVqaDemuxContext), 324cabdff1aSopenharmony_ci .read_probe = wsvqa_probe, 325cabdff1aSopenharmony_ci .read_header = wsvqa_read_header, 326cabdff1aSopenharmony_ci .read_packet = wsvqa_read_packet, 327cabdff1aSopenharmony_ci}; 328