1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Musepack SV8 demuxer 3cabdff1aSopenharmony_ci * Copyright (c) 2007 Konstantin Shishkov 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 "libavcodec/get_bits.h" 23cabdff1aSopenharmony_ci#include "libavcodec/unary.h" 24cabdff1aSopenharmony_ci#include "apetag.h" 25cabdff1aSopenharmony_ci#include "avformat.h" 26cabdff1aSopenharmony_ci#include "demux.h" 27cabdff1aSopenharmony_ci#include "internal.h" 28cabdff1aSopenharmony_ci#include "avio_internal.h" 29cabdff1aSopenharmony_ci 30cabdff1aSopenharmony_ci/// Two-byte MPC tag 31cabdff1aSopenharmony_ci#define MKMPCTAG(a, b) ((a) | ((b) << 8)) 32cabdff1aSopenharmony_ci 33cabdff1aSopenharmony_ci#define TAG_MPCK MKTAG('M','P','C','K') 34cabdff1aSopenharmony_ci 35cabdff1aSopenharmony_ci/// Reserved MPC tags 36cabdff1aSopenharmony_cienum MPCPacketTags{ 37cabdff1aSopenharmony_ci TAG_STREAMHDR = MKMPCTAG('S','H'), 38cabdff1aSopenharmony_ci TAG_STREAMEND = MKMPCTAG('S','E'), 39cabdff1aSopenharmony_ci 40cabdff1aSopenharmony_ci TAG_AUDIOPACKET = MKMPCTAG('A','P'), 41cabdff1aSopenharmony_ci 42cabdff1aSopenharmony_ci TAG_SEEKTBLOFF = MKMPCTAG('S','O'), 43cabdff1aSopenharmony_ci TAG_SEEKTABLE = MKMPCTAG('S','T'), 44cabdff1aSopenharmony_ci 45cabdff1aSopenharmony_ci TAG_REPLAYGAIN = MKMPCTAG('R','G'), 46cabdff1aSopenharmony_ci TAG_ENCINFO = MKMPCTAG('E','I'), 47cabdff1aSopenharmony_ci}; 48cabdff1aSopenharmony_ci 49cabdff1aSopenharmony_cistatic const int mpc8_rate[8] = { 44100, 48000, 37800, 32000, -1, -1, -1, -1 }; 50cabdff1aSopenharmony_ci 51cabdff1aSopenharmony_citypedef struct MPCContext { 52cabdff1aSopenharmony_ci int ver; 53cabdff1aSopenharmony_ci int64_t header_pos; 54cabdff1aSopenharmony_ci int64_t samples; 55cabdff1aSopenharmony_ci 56cabdff1aSopenharmony_ci int64_t apetag_start; 57cabdff1aSopenharmony_ci} MPCContext; 58cabdff1aSopenharmony_ci 59cabdff1aSopenharmony_cistatic inline int64_t bs_get_v(const uint8_t **bs) 60cabdff1aSopenharmony_ci{ 61cabdff1aSopenharmony_ci uint64_t v = 0; 62cabdff1aSopenharmony_ci int br = 0; 63cabdff1aSopenharmony_ci int c; 64cabdff1aSopenharmony_ci 65cabdff1aSopenharmony_ci do { 66cabdff1aSopenharmony_ci c = **bs; (*bs)++; 67cabdff1aSopenharmony_ci v <<= 7; 68cabdff1aSopenharmony_ci v |= c & 0x7F; 69cabdff1aSopenharmony_ci br++; 70cabdff1aSopenharmony_ci if (br > 10) 71cabdff1aSopenharmony_ci return -1; 72cabdff1aSopenharmony_ci } while (c & 0x80); 73cabdff1aSopenharmony_ci 74cabdff1aSopenharmony_ci return v - br; 75cabdff1aSopenharmony_ci} 76cabdff1aSopenharmony_ci 77cabdff1aSopenharmony_cistatic int mpc8_probe(const AVProbeData *p) 78cabdff1aSopenharmony_ci{ 79cabdff1aSopenharmony_ci const uint8_t *bs = p->buf + 4; 80cabdff1aSopenharmony_ci const uint8_t *bs_end = bs + p->buf_size; 81cabdff1aSopenharmony_ci int64_t size; 82cabdff1aSopenharmony_ci 83cabdff1aSopenharmony_ci if (p->buf_size < 16) 84cabdff1aSopenharmony_ci return 0; 85cabdff1aSopenharmony_ci if (AV_RL32(p->buf) != TAG_MPCK) 86cabdff1aSopenharmony_ci return 0; 87cabdff1aSopenharmony_ci while (bs < bs_end + 3) { 88cabdff1aSopenharmony_ci int header_found = (bs[0] == 'S' && bs[1] == 'H'); 89cabdff1aSopenharmony_ci if (bs[0] < 'A' || bs[0] > 'Z' || bs[1] < 'A' || bs[1] > 'Z') 90cabdff1aSopenharmony_ci return 0; 91cabdff1aSopenharmony_ci bs += 2; 92cabdff1aSopenharmony_ci size = bs_get_v(&bs); 93cabdff1aSopenharmony_ci if (size < 2) 94cabdff1aSopenharmony_ci return 0; 95cabdff1aSopenharmony_ci if (size >= bs_end - bs + 2) 96cabdff1aSopenharmony_ci return AVPROBE_SCORE_EXTENSION - 1; // seems to be valid MPC but no header yet 97cabdff1aSopenharmony_ci if (header_found) { 98cabdff1aSopenharmony_ci if (size < 11 || size > 28) 99cabdff1aSopenharmony_ci return 0; 100cabdff1aSopenharmony_ci if (!AV_RL32(bs)) //zero CRC is invalid 101cabdff1aSopenharmony_ci return 0; 102cabdff1aSopenharmony_ci return AVPROBE_SCORE_MAX; 103cabdff1aSopenharmony_ci } else { 104cabdff1aSopenharmony_ci bs += size - 2; 105cabdff1aSopenharmony_ci } 106cabdff1aSopenharmony_ci } 107cabdff1aSopenharmony_ci return 0; 108cabdff1aSopenharmony_ci} 109cabdff1aSopenharmony_ci 110cabdff1aSopenharmony_cistatic inline int64_t gb_get_v(GetBitContext *gb) 111cabdff1aSopenharmony_ci{ 112cabdff1aSopenharmony_ci uint64_t v = 0; 113cabdff1aSopenharmony_ci int bits = 0; 114cabdff1aSopenharmony_ci while(get_bits1(gb) && bits < 64-7){ 115cabdff1aSopenharmony_ci v <<= 7; 116cabdff1aSopenharmony_ci v |= get_bits(gb, 7); 117cabdff1aSopenharmony_ci bits += 7; 118cabdff1aSopenharmony_ci } 119cabdff1aSopenharmony_ci v <<= 7; 120cabdff1aSopenharmony_ci v |= get_bits(gb, 7); 121cabdff1aSopenharmony_ci 122cabdff1aSopenharmony_ci return v; 123cabdff1aSopenharmony_ci} 124cabdff1aSopenharmony_ci 125cabdff1aSopenharmony_cistatic void mpc8_get_chunk_header(AVIOContext *pb, int *tag, int64_t *size) 126cabdff1aSopenharmony_ci{ 127cabdff1aSopenharmony_ci int64_t pos; 128cabdff1aSopenharmony_ci pos = avio_tell(pb); 129cabdff1aSopenharmony_ci *tag = avio_rl16(pb); 130cabdff1aSopenharmony_ci *size = ffio_read_varlen(pb); 131cabdff1aSopenharmony_ci pos -= avio_tell(pb); 132cabdff1aSopenharmony_ci if (av_sat_add64(*size, pos) != (uint64_t)*size + pos) { 133cabdff1aSopenharmony_ci *size = -1; 134cabdff1aSopenharmony_ci } else 135cabdff1aSopenharmony_ci *size += pos; 136cabdff1aSopenharmony_ci} 137cabdff1aSopenharmony_ci 138cabdff1aSopenharmony_cistatic void mpc8_parse_seektable(AVFormatContext *s, int64_t off) 139cabdff1aSopenharmony_ci{ 140cabdff1aSopenharmony_ci MPCContext *c = s->priv_data; 141cabdff1aSopenharmony_ci int tag; 142cabdff1aSopenharmony_ci int64_t size, pos, ppos[2]; 143cabdff1aSopenharmony_ci uint8_t *buf; 144cabdff1aSopenharmony_ci int i, t, seekd, ret; 145cabdff1aSopenharmony_ci GetBitContext gb; 146cabdff1aSopenharmony_ci 147cabdff1aSopenharmony_ci if (s->nb_streams == 0) { 148cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "No stream added before parsing seek table\n"); 149cabdff1aSopenharmony_ci return; 150cabdff1aSopenharmony_ci } 151cabdff1aSopenharmony_ci 152cabdff1aSopenharmony_ci avio_seek(s->pb, off, SEEK_SET); 153cabdff1aSopenharmony_ci mpc8_get_chunk_header(s->pb, &tag, &size); 154cabdff1aSopenharmony_ci if(tag != TAG_SEEKTABLE){ 155cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "No seek table at given position\n"); 156cabdff1aSopenharmony_ci return; 157cabdff1aSopenharmony_ci } 158cabdff1aSopenharmony_ci if (size > INT_MAX/10 || size<=0) { 159cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Bad seek table size\n"); 160cabdff1aSopenharmony_ci return; 161cabdff1aSopenharmony_ci } 162cabdff1aSopenharmony_ci if(!(buf = av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE))) 163cabdff1aSopenharmony_ci return; 164cabdff1aSopenharmony_ci ret = avio_read(s->pb, buf, size); 165cabdff1aSopenharmony_ci if (ret != size) { 166cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "seek table truncated\n"); 167cabdff1aSopenharmony_ci av_free(buf); 168cabdff1aSopenharmony_ci return; 169cabdff1aSopenharmony_ci } 170cabdff1aSopenharmony_ci memset(buf+size, 0, AV_INPUT_BUFFER_PADDING_SIZE); 171cabdff1aSopenharmony_ci 172cabdff1aSopenharmony_ci init_get_bits(&gb, buf, size * 8); 173cabdff1aSopenharmony_ci size = gb_get_v(&gb); 174cabdff1aSopenharmony_ci if(size > UINT_MAX/4 || size > c->samples/1152){ 175cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Seek table is too big\n"); 176cabdff1aSopenharmony_ci av_free(buf); 177cabdff1aSopenharmony_ci return; 178cabdff1aSopenharmony_ci } 179cabdff1aSopenharmony_ci seekd = get_bits(&gb, 4); 180cabdff1aSopenharmony_ci for(i = 0; i < 2; i++){ 181cabdff1aSopenharmony_ci pos = gb_get_v(&gb); 182cabdff1aSopenharmony_ci if (av_sat_add64(pos, c->header_pos) != pos + (uint64_t)c->header_pos) { 183cabdff1aSopenharmony_ci av_free(buf); 184cabdff1aSopenharmony_ci return; 185cabdff1aSopenharmony_ci } 186cabdff1aSopenharmony_ci 187cabdff1aSopenharmony_ci pos += c->header_pos; 188cabdff1aSopenharmony_ci ppos[1 - i] = pos; 189cabdff1aSopenharmony_ci av_add_index_entry(s->streams[0], pos, i, 0, 0, AVINDEX_KEYFRAME); 190cabdff1aSopenharmony_ci } 191cabdff1aSopenharmony_ci for(; i < size; i++){ 192cabdff1aSopenharmony_ci if (get_bits_left(&gb) < 13) { 193cabdff1aSopenharmony_ci av_free(buf); 194cabdff1aSopenharmony_ci return; 195cabdff1aSopenharmony_ci } 196cabdff1aSopenharmony_ci t = get_unary(&gb, 1, 33) << 12; 197cabdff1aSopenharmony_ci t += get_bits(&gb, 12); 198cabdff1aSopenharmony_ci if(t & 1) 199cabdff1aSopenharmony_ci t = -(t & ~1); 200cabdff1aSopenharmony_ci pos = (t >> 1) + (uint64_t)ppos[0]*2 - ppos[1]; 201cabdff1aSopenharmony_ci av_add_index_entry(s->streams[0], pos, (int64_t)i << seekd, 0, 0, AVINDEX_KEYFRAME); 202cabdff1aSopenharmony_ci ppos[1] = ppos[0]; 203cabdff1aSopenharmony_ci ppos[0] = pos; 204cabdff1aSopenharmony_ci } 205cabdff1aSopenharmony_ci av_free(buf); 206cabdff1aSopenharmony_ci} 207cabdff1aSopenharmony_ci 208cabdff1aSopenharmony_cistatic void mpc8_handle_chunk(AVFormatContext *s, int tag, int64_t chunk_pos, int64_t size) 209cabdff1aSopenharmony_ci{ 210cabdff1aSopenharmony_ci AVIOContext *pb = s->pb; 211cabdff1aSopenharmony_ci int64_t pos, off; 212cabdff1aSopenharmony_ci 213cabdff1aSopenharmony_ci switch(tag){ 214cabdff1aSopenharmony_ci case TAG_SEEKTBLOFF: 215cabdff1aSopenharmony_ci pos = avio_tell(pb); 216cabdff1aSopenharmony_ci off = ffio_read_varlen(pb); 217cabdff1aSopenharmony_ci if (pos > INT64_MAX - size || off < 0 || off > INT64_MAX - chunk_pos) 218cabdff1aSopenharmony_ci return; 219cabdff1aSopenharmony_ci pos += size; 220cabdff1aSopenharmony_ci mpc8_parse_seektable(s, chunk_pos + off); 221cabdff1aSopenharmony_ci avio_seek(pb, pos, SEEK_SET); 222cabdff1aSopenharmony_ci break; 223cabdff1aSopenharmony_ci default: 224cabdff1aSopenharmony_ci avio_skip(pb, size); 225cabdff1aSopenharmony_ci } 226cabdff1aSopenharmony_ci} 227cabdff1aSopenharmony_ci 228cabdff1aSopenharmony_cistatic int mpc8_read_header(AVFormatContext *s) 229cabdff1aSopenharmony_ci{ 230cabdff1aSopenharmony_ci MPCContext *c = s->priv_data; 231cabdff1aSopenharmony_ci AVIOContext *pb = s->pb; 232cabdff1aSopenharmony_ci AVStream *st; 233cabdff1aSopenharmony_ci int tag = 0, ret; 234cabdff1aSopenharmony_ci int channels; 235cabdff1aSopenharmony_ci int64_t size, pos; 236cabdff1aSopenharmony_ci 237cabdff1aSopenharmony_ci c->header_pos = avio_tell(pb); 238cabdff1aSopenharmony_ci if(avio_rl32(pb) != TAG_MPCK){ 239cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Not a Musepack8 file\n"); 240cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 241cabdff1aSopenharmony_ci } 242cabdff1aSopenharmony_ci 243cabdff1aSopenharmony_ci while(!avio_feof(pb)){ 244cabdff1aSopenharmony_ci pos = avio_tell(pb); 245cabdff1aSopenharmony_ci mpc8_get_chunk_header(pb, &tag, &size); 246cabdff1aSopenharmony_ci if (size < 0) { 247cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Invalid chunk length\n"); 248cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 249cabdff1aSopenharmony_ci } 250cabdff1aSopenharmony_ci if(tag == TAG_STREAMHDR) 251cabdff1aSopenharmony_ci break; 252cabdff1aSopenharmony_ci mpc8_handle_chunk(s, tag, pos, size); 253cabdff1aSopenharmony_ci } 254cabdff1aSopenharmony_ci if(tag != TAG_STREAMHDR){ 255cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Stream header not found\n"); 256cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 257cabdff1aSopenharmony_ci } 258cabdff1aSopenharmony_ci pos = avio_tell(pb); 259cabdff1aSopenharmony_ci avio_skip(pb, 4); //CRC 260cabdff1aSopenharmony_ci c->ver = avio_r8(pb); 261cabdff1aSopenharmony_ci if(c->ver != 8){ 262cabdff1aSopenharmony_ci avpriv_report_missing_feature(s, "Stream version %d", c->ver); 263cabdff1aSopenharmony_ci return AVERROR_PATCHWELCOME; 264cabdff1aSopenharmony_ci } 265cabdff1aSopenharmony_ci c->samples = ffio_read_varlen(pb); 266cabdff1aSopenharmony_ci ffio_read_varlen(pb); //silence samples at the beginning 267cabdff1aSopenharmony_ci 268cabdff1aSopenharmony_ci st = avformat_new_stream(s, NULL); 269cabdff1aSopenharmony_ci if (!st) 270cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 271cabdff1aSopenharmony_ci st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; 272cabdff1aSopenharmony_ci st->codecpar->codec_id = AV_CODEC_ID_MUSEPACK8; 273cabdff1aSopenharmony_ci st->codecpar->bits_per_coded_sample = 16; 274cabdff1aSopenharmony_ci 275cabdff1aSopenharmony_ci if ((ret = ff_get_extradata(s, st->codecpar, pb, 2)) < 0) 276cabdff1aSopenharmony_ci return ret; 277cabdff1aSopenharmony_ci 278cabdff1aSopenharmony_ci channels = (st->codecpar->extradata[1] >> 4) + 1; 279cabdff1aSopenharmony_ci st->codecpar->ch_layout.nb_channels = channels; 280cabdff1aSopenharmony_ci st->codecpar->sample_rate = mpc8_rate[st->codecpar->extradata[0] >> 5]; 281cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 64, 1152 << (st->codecpar->extradata[1]&3)*2, st->codecpar->sample_rate); 282cabdff1aSopenharmony_ci st->start_time = 0; 283cabdff1aSopenharmony_ci st->duration = c->samples / (1152 << (st->codecpar->extradata[1]&3)*2); 284cabdff1aSopenharmony_ci size -= avio_tell(pb) - pos; 285cabdff1aSopenharmony_ci if (size > 0) 286cabdff1aSopenharmony_ci avio_skip(pb, size); 287cabdff1aSopenharmony_ci 288cabdff1aSopenharmony_ci if (pb->seekable & AVIO_SEEKABLE_NORMAL) { 289cabdff1aSopenharmony_ci int64_t pos = avio_tell(s->pb); 290cabdff1aSopenharmony_ci c->apetag_start = ff_ape_parse_tag(s); 291cabdff1aSopenharmony_ci avio_seek(s->pb, pos, SEEK_SET); 292cabdff1aSopenharmony_ci } 293cabdff1aSopenharmony_ci 294cabdff1aSopenharmony_ci return 0; 295cabdff1aSopenharmony_ci} 296cabdff1aSopenharmony_ci 297cabdff1aSopenharmony_cistatic int mpc8_read_packet(AVFormatContext *s, AVPacket *pkt) 298cabdff1aSopenharmony_ci{ 299cabdff1aSopenharmony_ci MPCContext *c = s->priv_data; 300cabdff1aSopenharmony_ci int tag, ret; 301cabdff1aSopenharmony_ci int64_t pos, size; 302cabdff1aSopenharmony_ci 303cabdff1aSopenharmony_ci while(!avio_feof(s->pb)){ 304cabdff1aSopenharmony_ci pos = avio_tell(s->pb); 305cabdff1aSopenharmony_ci 306cabdff1aSopenharmony_ci /* don't return bogus packets with the ape tag data */ 307cabdff1aSopenharmony_ci if (c->apetag_start && pos >= c->apetag_start) 308cabdff1aSopenharmony_ci return AVERROR_EOF; 309cabdff1aSopenharmony_ci 310cabdff1aSopenharmony_ci mpc8_get_chunk_header(s->pb, &tag, &size); 311cabdff1aSopenharmony_ci if (size < 0 || size > INT_MAX) 312cabdff1aSopenharmony_ci return -1; 313cabdff1aSopenharmony_ci if(tag == TAG_AUDIOPACKET){ 314cabdff1aSopenharmony_ci if ((ret = av_get_packet(s->pb, pkt, size)) < 0) 315cabdff1aSopenharmony_ci return ret; 316cabdff1aSopenharmony_ci pkt->stream_index = 0; 317cabdff1aSopenharmony_ci pkt->duration = 1; 318cabdff1aSopenharmony_ci return 0; 319cabdff1aSopenharmony_ci } 320cabdff1aSopenharmony_ci if(tag == TAG_STREAMEND) 321cabdff1aSopenharmony_ci return AVERROR_EOF; 322cabdff1aSopenharmony_ci mpc8_handle_chunk(s, tag, pos, size); 323cabdff1aSopenharmony_ci } 324cabdff1aSopenharmony_ci return AVERROR_EOF; 325cabdff1aSopenharmony_ci} 326cabdff1aSopenharmony_ci 327cabdff1aSopenharmony_cistatic int mpc8_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) 328cabdff1aSopenharmony_ci{ 329cabdff1aSopenharmony_ci AVStream *st = s->streams[stream_index]; 330cabdff1aSopenharmony_ci FFStream *const sti = ffstream(st); 331cabdff1aSopenharmony_ci int index = av_index_search_timestamp(st, timestamp, flags); 332cabdff1aSopenharmony_ci 333cabdff1aSopenharmony_ci if(index < 0) return -1; 334cabdff1aSopenharmony_ci if (avio_seek(s->pb, sti->index_entries[index].pos, SEEK_SET) < 0) 335cabdff1aSopenharmony_ci return -1; 336cabdff1aSopenharmony_ci avpriv_update_cur_dts(s, st, sti->index_entries[index].timestamp); 337cabdff1aSopenharmony_ci return 0; 338cabdff1aSopenharmony_ci} 339cabdff1aSopenharmony_ci 340cabdff1aSopenharmony_ci 341cabdff1aSopenharmony_ciconst AVInputFormat ff_mpc8_demuxer = { 342cabdff1aSopenharmony_ci .name = "mpc8", 343cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Musepack SV8"), 344cabdff1aSopenharmony_ci .priv_data_size = sizeof(MPCContext), 345cabdff1aSopenharmony_ci .read_probe = mpc8_probe, 346cabdff1aSopenharmony_ci .read_header = mpc8_read_header, 347cabdff1aSopenharmony_ci .read_packet = mpc8_read_packet, 348cabdff1aSopenharmony_ci .read_seek = mpc8_read_seek, 349cabdff1aSopenharmony_ci}; 350