1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Sierra VMD Format Demuxer
3cabdff1aSopenharmony_ci * Copyright (c) 2004 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 * Sierra VMD file demuxer
25cabdff1aSopenharmony_ci * by Vladimir "VAG" Gneushev (vagsoft at mail.ru)
26cabdff1aSopenharmony_ci * for more information on the Sierra VMD file format, visit:
27cabdff1aSopenharmony_ci *   http://www.pcisys.net/~melanson/codecs/
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 VMD_HEADER_SIZE 0x0330
37cabdff1aSopenharmony_ci#define BYTES_PER_FRAME_RECORD 16
38cabdff1aSopenharmony_ci
39cabdff1aSopenharmony_citypedef struct vmd_frame {
40cabdff1aSopenharmony_ci  int stream_index;
41cabdff1aSopenharmony_ci  unsigned int frame_size;
42cabdff1aSopenharmony_ci  int64_t frame_offset;
43cabdff1aSopenharmony_ci  int64_t pts;
44cabdff1aSopenharmony_ci  unsigned char frame_record[BYTES_PER_FRAME_RECORD];
45cabdff1aSopenharmony_ci} vmd_frame;
46cabdff1aSopenharmony_ci
47cabdff1aSopenharmony_citypedef struct VmdDemuxContext {
48cabdff1aSopenharmony_ci    int video_stream_index;
49cabdff1aSopenharmony_ci    int audio_stream_index;
50cabdff1aSopenharmony_ci
51cabdff1aSopenharmony_ci    unsigned int frame_count;
52cabdff1aSopenharmony_ci    unsigned int frames_per_block;
53cabdff1aSopenharmony_ci    vmd_frame *frame_table;
54cabdff1aSopenharmony_ci    unsigned int current_frame;
55cabdff1aSopenharmony_ci    int is_indeo3;
56cabdff1aSopenharmony_ci
57cabdff1aSopenharmony_ci    int sample_rate;
58cabdff1aSopenharmony_ci    int64_t audio_sample_counter;
59cabdff1aSopenharmony_ci    int skiphdr;
60cabdff1aSopenharmony_ci
61cabdff1aSopenharmony_ci    unsigned char vmd_header[VMD_HEADER_SIZE];
62cabdff1aSopenharmony_ci} VmdDemuxContext;
63cabdff1aSopenharmony_ci
64cabdff1aSopenharmony_cistatic int vmd_probe(const AVProbeData *p)
65cabdff1aSopenharmony_ci{
66cabdff1aSopenharmony_ci    int w, h, sample_rate;
67cabdff1aSopenharmony_ci    if (p->buf_size < 806)
68cabdff1aSopenharmony_ci        return 0;
69cabdff1aSopenharmony_ci    /* check if the first 2 bytes of the file contain the appropriate size
70cabdff1aSopenharmony_ci     * of a VMD header chunk */
71cabdff1aSopenharmony_ci    if (AV_RL16(&p->buf[0]) != VMD_HEADER_SIZE - 2)
72cabdff1aSopenharmony_ci        return 0;
73cabdff1aSopenharmony_ci    w = AV_RL16(&p->buf[12]);
74cabdff1aSopenharmony_ci    h = AV_RL16(&p->buf[14]);
75cabdff1aSopenharmony_ci    sample_rate = AV_RL16(&p->buf[804]);
76cabdff1aSopenharmony_ci    if ((!w || w > 2048 || !h || h > 2048) &&
77cabdff1aSopenharmony_ci        sample_rate != 22050)
78cabdff1aSopenharmony_ci        return 0;
79cabdff1aSopenharmony_ci
80cabdff1aSopenharmony_ci    /* only return half certainty since this check is a bit sketchy */
81cabdff1aSopenharmony_ci    return AVPROBE_SCORE_EXTENSION;
82cabdff1aSopenharmony_ci}
83cabdff1aSopenharmony_ci
84cabdff1aSopenharmony_cistatic int vmd_read_header(AVFormatContext *s)
85cabdff1aSopenharmony_ci{
86cabdff1aSopenharmony_ci    VmdDemuxContext *vmd = s->priv_data;
87cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
88cabdff1aSopenharmony_ci    AVStream *st = NULL, *vst = NULL;
89cabdff1aSopenharmony_ci    unsigned int toc_offset;
90cabdff1aSopenharmony_ci    unsigned char *raw_frame_table;
91cabdff1aSopenharmony_ci    int raw_frame_table_size;
92cabdff1aSopenharmony_ci    int64_t current_offset;
93cabdff1aSopenharmony_ci    int i, j, ret;
94cabdff1aSopenharmony_ci    int width, height;
95cabdff1aSopenharmony_ci    unsigned int total_frames;
96cabdff1aSopenharmony_ci    int64_t current_audio_pts = 0;
97cabdff1aSopenharmony_ci    unsigned char chunk[BYTES_PER_FRAME_RECORD];
98cabdff1aSopenharmony_ci    int num, den;
99cabdff1aSopenharmony_ci    int sound_buffers;
100cabdff1aSopenharmony_ci
101cabdff1aSopenharmony_ci    /* fetch the main header, including the 2 header length bytes */
102cabdff1aSopenharmony_ci    avio_seek(pb, 0, SEEK_SET);
103cabdff1aSopenharmony_ci    if (avio_read(pb, vmd->vmd_header, VMD_HEADER_SIZE) != VMD_HEADER_SIZE)
104cabdff1aSopenharmony_ci        return AVERROR(EIO);
105cabdff1aSopenharmony_ci
106cabdff1aSopenharmony_ci    width = AV_RL16(&vmd->vmd_header[12]);
107cabdff1aSopenharmony_ci    height = AV_RL16(&vmd->vmd_header[14]);
108cabdff1aSopenharmony_ci    if (width && height) {
109cabdff1aSopenharmony_ci        if(vmd->vmd_header[24] == 'i' && vmd->vmd_header[25] == 'v' && vmd->vmd_header[26] == '3') {
110cabdff1aSopenharmony_ci            vmd->is_indeo3 = 1;
111cabdff1aSopenharmony_ci        } else {
112cabdff1aSopenharmony_ci            vmd->is_indeo3 = 0;
113cabdff1aSopenharmony_ci        }
114cabdff1aSopenharmony_ci        /* start up the decoders */
115cabdff1aSopenharmony_ci        vst = avformat_new_stream(s, NULL);
116cabdff1aSopenharmony_ci        if (!vst)
117cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
118cabdff1aSopenharmony_ci        avpriv_set_pts_info(vst, 33, 1, 10);
119cabdff1aSopenharmony_ci        vmd->video_stream_index = vst->index;
120cabdff1aSopenharmony_ci        vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
121cabdff1aSopenharmony_ci        vst->codecpar->codec_id = vmd->is_indeo3 ? AV_CODEC_ID_INDEO3 : AV_CODEC_ID_VMDVIDEO;
122cabdff1aSopenharmony_ci        vst->codecpar->codec_tag = 0;  /* no fourcc */
123cabdff1aSopenharmony_ci        vst->codecpar->width = width;
124cabdff1aSopenharmony_ci        vst->codecpar->height = height;
125cabdff1aSopenharmony_ci        if(vmd->is_indeo3 && vst->codecpar->width > 320){
126cabdff1aSopenharmony_ci            vst->codecpar->width >>= 1;
127cabdff1aSopenharmony_ci            vst->codecpar->height >>= 1;
128cabdff1aSopenharmony_ci        }
129cabdff1aSopenharmony_ci        if ((ret = ff_alloc_extradata(vst->codecpar, VMD_HEADER_SIZE)) < 0)
130cabdff1aSopenharmony_ci            return ret;
131cabdff1aSopenharmony_ci        memcpy(vst->codecpar->extradata, vmd->vmd_header, VMD_HEADER_SIZE);
132cabdff1aSopenharmony_ci    }
133cabdff1aSopenharmony_ci
134cabdff1aSopenharmony_ci    /* if sample rate is 0, assume no audio */
135cabdff1aSopenharmony_ci    vmd->sample_rate = AV_RL16(&vmd->vmd_header[804]);
136cabdff1aSopenharmony_ci    if (vmd->sample_rate) {
137cabdff1aSopenharmony_ci        int channels;
138cabdff1aSopenharmony_ci        st = avformat_new_stream(s, NULL);
139cabdff1aSopenharmony_ci        if (!st)
140cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
141cabdff1aSopenharmony_ci        vmd->audio_stream_index = st->index;
142cabdff1aSopenharmony_ci        st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
143cabdff1aSopenharmony_ci        st->codecpar->codec_id   = AV_CODEC_ID_VMDAUDIO;
144cabdff1aSopenharmony_ci        st->codecpar->codec_tag  = 0;  /* no fourcc */
145cabdff1aSopenharmony_ci        st->codecpar->sample_rate = vmd->sample_rate;
146cabdff1aSopenharmony_ci        st->codecpar->block_align = AV_RL16(&vmd->vmd_header[806]);
147cabdff1aSopenharmony_ci        if (st->codecpar->block_align & 0x8000) {
148cabdff1aSopenharmony_ci            st->codecpar->bits_per_coded_sample = 16;
149cabdff1aSopenharmony_ci            st->codecpar->block_align = -(st->codecpar->block_align - 0x10000);
150cabdff1aSopenharmony_ci        } else {
151cabdff1aSopenharmony_ci            st->codecpar->bits_per_coded_sample = 8;
152cabdff1aSopenharmony_ci        }
153cabdff1aSopenharmony_ci        if (vmd->vmd_header[811] & 0x80) {
154cabdff1aSopenharmony_ci            channels = 2;
155cabdff1aSopenharmony_ci        } else if (vmd->vmd_header[811] & 0x2) {
156cabdff1aSopenharmony_ci            /* Shivers 2 stereo audio */
157cabdff1aSopenharmony_ci            /* Frame length is for 1 channel */
158cabdff1aSopenharmony_ci            channels        = 2;
159cabdff1aSopenharmony_ci            st->codecpar->block_align = st->codecpar->block_align << 1;
160cabdff1aSopenharmony_ci        } else {
161cabdff1aSopenharmony_ci            channels = 1;
162cabdff1aSopenharmony_ci        }
163cabdff1aSopenharmony_ci        av_channel_layout_default(&st->codecpar->ch_layout, channels);
164cabdff1aSopenharmony_ci        st->codecpar->bit_rate = st->codecpar->sample_rate *
165cabdff1aSopenharmony_ci            st->codecpar->bits_per_coded_sample * channels;
166cabdff1aSopenharmony_ci
167cabdff1aSopenharmony_ci        /* calculate pts */
168cabdff1aSopenharmony_ci        num = st->codecpar->block_align;
169cabdff1aSopenharmony_ci        den = st->codecpar->sample_rate * channels;
170cabdff1aSopenharmony_ci        av_reduce(&num, &den, num, den, (1UL<<31)-1);
171cabdff1aSopenharmony_ci        if (vst)
172cabdff1aSopenharmony_ci            avpriv_set_pts_info(vst, 33, num, den);
173cabdff1aSopenharmony_ci        avpriv_set_pts_info(st, 33, num, den);
174cabdff1aSopenharmony_ci    }
175cabdff1aSopenharmony_ci    if (!s->nb_streams)
176cabdff1aSopenharmony_ci        return AVERROR_INVALIDDATA;
177cabdff1aSopenharmony_ci
178cabdff1aSopenharmony_ci    toc_offset = AV_RL32(&vmd->vmd_header[812]);
179cabdff1aSopenharmony_ci    vmd->frame_count = AV_RL16(&vmd->vmd_header[6]);
180cabdff1aSopenharmony_ci    vmd->frames_per_block = AV_RL16(&vmd->vmd_header[18]);
181cabdff1aSopenharmony_ci    avio_seek(pb, toc_offset, SEEK_SET);
182cabdff1aSopenharmony_ci
183cabdff1aSopenharmony_ci    raw_frame_table = NULL;
184cabdff1aSopenharmony_ci    vmd->frame_table = NULL;
185cabdff1aSopenharmony_ci    sound_buffers = AV_RL16(&vmd->vmd_header[808]);
186cabdff1aSopenharmony_ci    raw_frame_table_size = vmd->frame_count * 6;
187cabdff1aSopenharmony_ci    raw_frame_table = av_malloc(raw_frame_table_size);
188cabdff1aSopenharmony_ci    vmd->frame_table = av_malloc_array(vmd->frame_count * vmd->frames_per_block + sound_buffers, sizeof(vmd_frame));
189cabdff1aSopenharmony_ci    if (!raw_frame_table || !vmd->frame_table) {
190cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
191cabdff1aSopenharmony_ci        goto error;
192cabdff1aSopenharmony_ci    }
193cabdff1aSopenharmony_ci    if (avio_read(pb, raw_frame_table, raw_frame_table_size) !=
194cabdff1aSopenharmony_ci        raw_frame_table_size) {
195cabdff1aSopenharmony_ci        ret = AVERROR(EIO);
196cabdff1aSopenharmony_ci        goto error;
197cabdff1aSopenharmony_ci    }
198cabdff1aSopenharmony_ci
199cabdff1aSopenharmony_ci    total_frames = 0;
200cabdff1aSopenharmony_ci    for (i = 0; i < vmd->frame_count; i++) {
201cabdff1aSopenharmony_ci
202cabdff1aSopenharmony_ci        current_offset = AV_RL32(&raw_frame_table[6 * i + 2]);
203cabdff1aSopenharmony_ci
204cabdff1aSopenharmony_ci        /* handle each entry in index block */
205cabdff1aSopenharmony_ci        for (j = 0; j < vmd->frames_per_block; j++) {
206cabdff1aSopenharmony_ci            int type;
207cabdff1aSopenharmony_ci            uint32_t size;
208cabdff1aSopenharmony_ci
209cabdff1aSopenharmony_ci            if ((ret = avio_read(pb, chunk, BYTES_PER_FRAME_RECORD)) != BYTES_PER_FRAME_RECORD) {
210cabdff1aSopenharmony_ci                av_log(s, AV_LOG_ERROR, "Failed to read frame record\n");
211cabdff1aSopenharmony_ci                if (ret >= 0)
212cabdff1aSopenharmony_ci                    ret = AVERROR_INVALIDDATA;
213cabdff1aSopenharmony_ci                goto error;
214cabdff1aSopenharmony_ci            }
215cabdff1aSopenharmony_ci            type = chunk[0];
216cabdff1aSopenharmony_ci            size = AV_RL32(&chunk[2]);
217cabdff1aSopenharmony_ci            if (size > INT_MAX / 2) {
218cabdff1aSopenharmony_ci                av_log(s, AV_LOG_ERROR, "Invalid frame size\n");
219cabdff1aSopenharmony_ci                ret = AVERROR_INVALIDDATA;
220cabdff1aSopenharmony_ci                goto error;
221cabdff1aSopenharmony_ci            }
222cabdff1aSopenharmony_ci            if(!size && type != 1)
223cabdff1aSopenharmony_ci                continue;
224cabdff1aSopenharmony_ci            switch(type) {
225cabdff1aSopenharmony_ci            case 1: /* Audio Chunk */
226cabdff1aSopenharmony_ci                if (!st) break;
227cabdff1aSopenharmony_ci                /* first audio chunk contains several audio buffers */
228cabdff1aSopenharmony_ci                vmd->frame_table[total_frames].frame_offset = current_offset;
229cabdff1aSopenharmony_ci                vmd->frame_table[total_frames].stream_index = vmd->audio_stream_index;
230cabdff1aSopenharmony_ci                vmd->frame_table[total_frames].frame_size = size;
231cabdff1aSopenharmony_ci                memcpy(vmd->frame_table[total_frames].frame_record, chunk, BYTES_PER_FRAME_RECORD);
232cabdff1aSopenharmony_ci                vmd->frame_table[total_frames].pts = current_audio_pts;
233cabdff1aSopenharmony_ci                total_frames++;
234cabdff1aSopenharmony_ci                if(!current_audio_pts)
235cabdff1aSopenharmony_ci                    current_audio_pts += sound_buffers - 1;
236cabdff1aSopenharmony_ci                else
237cabdff1aSopenharmony_ci                    current_audio_pts++;
238cabdff1aSopenharmony_ci                break;
239cabdff1aSopenharmony_ci            case 2: /* Video Chunk */
240cabdff1aSopenharmony_ci                if (!vst)
241cabdff1aSopenharmony_ci                    break;
242cabdff1aSopenharmony_ci                vmd->frame_table[total_frames].frame_offset = current_offset;
243cabdff1aSopenharmony_ci                vmd->frame_table[total_frames].stream_index = vmd->video_stream_index;
244cabdff1aSopenharmony_ci                vmd->frame_table[total_frames].frame_size = size;
245cabdff1aSopenharmony_ci                memcpy(vmd->frame_table[total_frames].frame_record, chunk, BYTES_PER_FRAME_RECORD);
246cabdff1aSopenharmony_ci                vmd->frame_table[total_frames].pts = i;
247cabdff1aSopenharmony_ci                total_frames++;
248cabdff1aSopenharmony_ci                break;
249cabdff1aSopenharmony_ci            }
250cabdff1aSopenharmony_ci            current_offset += size;
251cabdff1aSopenharmony_ci        }
252cabdff1aSopenharmony_ci    }
253cabdff1aSopenharmony_ci
254cabdff1aSopenharmony_ci
255cabdff1aSopenharmony_ci    vmd->current_frame = 0;
256cabdff1aSopenharmony_ci    vmd->frame_count = total_frames;
257cabdff1aSopenharmony_ci
258cabdff1aSopenharmony_ci    ret = 0;
259cabdff1aSopenharmony_cierror:
260cabdff1aSopenharmony_ci    av_freep(&raw_frame_table);
261cabdff1aSopenharmony_ci    return ret;
262cabdff1aSopenharmony_ci}
263cabdff1aSopenharmony_ci
264cabdff1aSopenharmony_cistatic int vmd_read_packet(AVFormatContext *s,
265cabdff1aSopenharmony_ci                           AVPacket *pkt)
266cabdff1aSopenharmony_ci{
267cabdff1aSopenharmony_ci    VmdDemuxContext *vmd = s->priv_data;
268cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
269cabdff1aSopenharmony_ci    int ret = 0;
270cabdff1aSopenharmony_ci    vmd_frame *frame;
271cabdff1aSopenharmony_ci
272cabdff1aSopenharmony_ci    if (vmd->current_frame >= vmd->frame_count)
273cabdff1aSopenharmony_ci        return AVERROR_EOF;
274cabdff1aSopenharmony_ci
275cabdff1aSopenharmony_ci    frame = &vmd->frame_table[vmd->current_frame];
276cabdff1aSopenharmony_ci    /* position the stream (will probably be there already) */
277cabdff1aSopenharmony_ci    avio_seek(pb, frame->frame_offset, SEEK_SET);
278cabdff1aSopenharmony_ci
279cabdff1aSopenharmony_ci    if(ffio_limit(pb, frame->frame_size) != frame->frame_size)
280cabdff1aSopenharmony_ci        return AVERROR(EIO);
281cabdff1aSopenharmony_ci    ret = av_new_packet(pkt, frame->frame_size + BYTES_PER_FRAME_RECORD);
282cabdff1aSopenharmony_ci    if (ret < 0)
283cabdff1aSopenharmony_ci        return ret;
284cabdff1aSopenharmony_ci    pkt->pos= avio_tell(pb);
285cabdff1aSopenharmony_ci    memcpy(pkt->data, frame->frame_record, BYTES_PER_FRAME_RECORD);
286cabdff1aSopenharmony_ci    if(vmd->is_indeo3 && frame->frame_record[0] == 0x02)
287cabdff1aSopenharmony_ci        ret = avio_read(pb, pkt->data, frame->frame_size);
288cabdff1aSopenharmony_ci    else
289cabdff1aSopenharmony_ci        ret = avio_read(pb, pkt->data + BYTES_PER_FRAME_RECORD,
290cabdff1aSopenharmony_ci            frame->frame_size);
291cabdff1aSopenharmony_ci
292cabdff1aSopenharmony_ci    if (ret != frame->frame_size) {
293cabdff1aSopenharmony_ci        ret = AVERROR(EIO);
294cabdff1aSopenharmony_ci    }
295cabdff1aSopenharmony_ci    pkt->stream_index = frame->stream_index;
296cabdff1aSopenharmony_ci    pkt->pts = frame->pts;
297cabdff1aSopenharmony_ci    av_log(s, AV_LOG_DEBUG, " dispatching %s frame with %d bytes and pts %"PRId64"\n",
298cabdff1aSopenharmony_ci            (frame->frame_record[0] == 0x02) ? "video" : "audio",
299cabdff1aSopenharmony_ci            frame->frame_size + BYTES_PER_FRAME_RECORD,
300cabdff1aSopenharmony_ci            pkt->pts);
301cabdff1aSopenharmony_ci
302cabdff1aSopenharmony_ci    vmd->current_frame++;
303cabdff1aSopenharmony_ci
304cabdff1aSopenharmony_ci    return ret;
305cabdff1aSopenharmony_ci}
306cabdff1aSopenharmony_ci
307cabdff1aSopenharmony_cistatic int vmd_read_close(AVFormatContext *s)
308cabdff1aSopenharmony_ci{
309cabdff1aSopenharmony_ci    VmdDemuxContext *vmd = s->priv_data;
310cabdff1aSopenharmony_ci
311cabdff1aSopenharmony_ci    av_freep(&vmd->frame_table);
312cabdff1aSopenharmony_ci
313cabdff1aSopenharmony_ci    return 0;
314cabdff1aSopenharmony_ci}
315cabdff1aSopenharmony_ci
316cabdff1aSopenharmony_ciconst AVInputFormat ff_vmd_demuxer = {
317cabdff1aSopenharmony_ci    .name           = "vmd",
318cabdff1aSopenharmony_ci    .long_name      = NULL_IF_CONFIG_SMALL("Sierra VMD"),
319cabdff1aSopenharmony_ci    .priv_data_size = sizeof(VmdDemuxContext),
320cabdff1aSopenharmony_ci    .flags_internal = FF_FMT_INIT_CLEANUP,
321cabdff1aSopenharmony_ci    .read_probe     = vmd_probe,
322cabdff1aSopenharmony_ci    .read_header    = vmd_read_header,
323cabdff1aSopenharmony_ci    .read_packet    = vmd_read_packet,
324cabdff1aSopenharmony_ci    .read_close     = vmd_read_close,
325cabdff1aSopenharmony_ci};
326