1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * CDXL video decoder 3cabdff1aSopenharmony_ci * Copyright (c) 2011-2012 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/** 23cabdff1aSopenharmony_ci * @file 24cabdff1aSopenharmony_ci * Commodore CDXL video decoder 25cabdff1aSopenharmony_ci * @author Paul B Mahol 26cabdff1aSopenharmony_ci */ 27cabdff1aSopenharmony_ci 28cabdff1aSopenharmony_ci#define UNCHECKED_BITSTREAM_READER 1 29cabdff1aSopenharmony_ci 30cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h" 31cabdff1aSopenharmony_ci#include "libavutil/imgutils.h" 32cabdff1aSopenharmony_ci#include "avcodec.h" 33cabdff1aSopenharmony_ci#include "bytestream.h" 34cabdff1aSopenharmony_ci#include "codec_internal.h" 35cabdff1aSopenharmony_ci#include "get_bits.h" 36cabdff1aSopenharmony_ci#include "internal.h" 37cabdff1aSopenharmony_ci 38cabdff1aSopenharmony_ci#define BIT_PLANAR 0x00 39cabdff1aSopenharmony_ci#define CHUNKY 0x20 40cabdff1aSopenharmony_ci#define BYTE_PLANAR 0x40 41cabdff1aSopenharmony_ci#define BIT_LINE 0x80 42cabdff1aSopenharmony_ci#define BYTE_LINE 0xC0 43cabdff1aSopenharmony_ci 44cabdff1aSopenharmony_citypedef struct CDXLVideoContext { 45cabdff1aSopenharmony_ci AVCodecContext *avctx; 46cabdff1aSopenharmony_ci int bpp; 47cabdff1aSopenharmony_ci int type; 48cabdff1aSopenharmony_ci int format; 49cabdff1aSopenharmony_ci int padded_bits; 50cabdff1aSopenharmony_ci const uint8_t *palette; 51cabdff1aSopenharmony_ci int palette_size; 52cabdff1aSopenharmony_ci const uint8_t *video; 53cabdff1aSopenharmony_ci int video_size; 54cabdff1aSopenharmony_ci uint8_t *new_video; 55cabdff1aSopenharmony_ci int new_video_size; 56cabdff1aSopenharmony_ci} CDXLVideoContext; 57cabdff1aSopenharmony_ci 58cabdff1aSopenharmony_cistatic av_cold int cdxl_decode_init(AVCodecContext *avctx) 59cabdff1aSopenharmony_ci{ 60cabdff1aSopenharmony_ci CDXLVideoContext *c = avctx->priv_data; 61cabdff1aSopenharmony_ci 62cabdff1aSopenharmony_ci c->new_video_size = 0; 63cabdff1aSopenharmony_ci c->avctx = avctx; 64cabdff1aSopenharmony_ci 65cabdff1aSopenharmony_ci return 0; 66cabdff1aSopenharmony_ci} 67cabdff1aSopenharmony_ci 68cabdff1aSopenharmony_cistatic void import_palette(CDXLVideoContext *c, uint32_t *new_palette) 69cabdff1aSopenharmony_ci{ 70cabdff1aSopenharmony_ci if (c->type == 1) { 71cabdff1aSopenharmony_ci for (int i = 0; i < c->palette_size / 2; i++) { 72cabdff1aSopenharmony_ci unsigned rgb = AV_RB16(&c->palette[i * 2]); 73cabdff1aSopenharmony_ci unsigned r = ((rgb >> 8) & 0xF) * 0x11; 74cabdff1aSopenharmony_ci unsigned g = ((rgb >> 4) & 0xF) * 0x11; 75cabdff1aSopenharmony_ci unsigned b = (rgb & 0xF) * 0x11; 76cabdff1aSopenharmony_ci AV_WN32(&new_palette[i], (0xFFU << 24) | (r << 16) | (g << 8) | b); 77cabdff1aSopenharmony_ci } 78cabdff1aSopenharmony_ci } else { 79cabdff1aSopenharmony_ci for (int i = 0; i < c->palette_size / 3; i++) { 80cabdff1aSopenharmony_ci unsigned rgb = AV_RB24(&c->palette[i * 3]); 81cabdff1aSopenharmony_ci AV_WN32(&new_palette[i], (0xFFU << 24) | rgb); 82cabdff1aSopenharmony_ci } 83cabdff1aSopenharmony_ci } 84cabdff1aSopenharmony_ci} 85cabdff1aSopenharmony_ci 86cabdff1aSopenharmony_cistatic void bitplanar2chunky(CDXLVideoContext *c, int linesize, uint8_t *out) 87cabdff1aSopenharmony_ci{ 88cabdff1aSopenharmony_ci GetBitContext gb; 89cabdff1aSopenharmony_ci int x, y, plane; 90cabdff1aSopenharmony_ci 91cabdff1aSopenharmony_ci if (init_get_bits8(&gb, c->video, c->video_size) < 0) 92cabdff1aSopenharmony_ci return; 93cabdff1aSopenharmony_ci for (plane = 0; plane < c->bpp; plane++) { 94cabdff1aSopenharmony_ci for (y = 0; y < c->avctx->height; y++) { 95cabdff1aSopenharmony_ci for (x = 0; x < c->avctx->width; x++) 96cabdff1aSopenharmony_ci out[linesize * y + x] |= get_bits1(&gb) << plane; 97cabdff1aSopenharmony_ci skip_bits(&gb, c->padded_bits); 98cabdff1aSopenharmony_ci } 99cabdff1aSopenharmony_ci } 100cabdff1aSopenharmony_ci} 101cabdff1aSopenharmony_ci 102cabdff1aSopenharmony_cistatic void bitline2chunky(CDXLVideoContext *c, int linesize, uint8_t *out) 103cabdff1aSopenharmony_ci{ 104cabdff1aSopenharmony_ci GetBitContext gb; 105cabdff1aSopenharmony_ci int x, y, plane; 106cabdff1aSopenharmony_ci 107cabdff1aSopenharmony_ci if (init_get_bits8(&gb, c->video, c->video_size) < 0) 108cabdff1aSopenharmony_ci return; 109cabdff1aSopenharmony_ci for (y = 0; y < c->avctx->height; y++) { 110cabdff1aSopenharmony_ci for (plane = 0; plane < c->bpp; plane++) { 111cabdff1aSopenharmony_ci for (x = 0; x < c->avctx->width; x++) 112cabdff1aSopenharmony_ci out[linesize * y + x] |= get_bits1(&gb) << plane; 113cabdff1aSopenharmony_ci skip_bits(&gb, c->padded_bits); 114cabdff1aSopenharmony_ci } 115cabdff1aSopenharmony_ci } 116cabdff1aSopenharmony_ci} 117cabdff1aSopenharmony_ci 118cabdff1aSopenharmony_cistatic void chunky2chunky(CDXLVideoContext *c, int linesize, uint8_t *out) 119cabdff1aSopenharmony_ci{ 120cabdff1aSopenharmony_ci GetByteContext gb; 121cabdff1aSopenharmony_ci int y; 122cabdff1aSopenharmony_ci 123cabdff1aSopenharmony_ci bytestream2_init(&gb, c->video, c->video_size); 124cabdff1aSopenharmony_ci for (y = 0; y < c->avctx->height; y++) { 125cabdff1aSopenharmony_ci bytestream2_get_buffer(&gb, out + linesize * y, c->avctx->width * 3); 126cabdff1aSopenharmony_ci } 127cabdff1aSopenharmony_ci} 128cabdff1aSopenharmony_ci 129cabdff1aSopenharmony_cistatic void import_format(CDXLVideoContext *c, int linesize, uint8_t *out) 130cabdff1aSopenharmony_ci{ 131cabdff1aSopenharmony_ci memset(out, 0, linesize * c->avctx->height); 132cabdff1aSopenharmony_ci 133cabdff1aSopenharmony_ci switch (c->format) { 134cabdff1aSopenharmony_ci case BIT_PLANAR: 135cabdff1aSopenharmony_ci bitplanar2chunky(c, linesize, out); 136cabdff1aSopenharmony_ci break; 137cabdff1aSopenharmony_ci case BIT_LINE: 138cabdff1aSopenharmony_ci bitline2chunky(c, linesize, out); 139cabdff1aSopenharmony_ci break; 140cabdff1aSopenharmony_ci case CHUNKY: 141cabdff1aSopenharmony_ci chunky2chunky(c, linesize, out); 142cabdff1aSopenharmony_ci break; 143cabdff1aSopenharmony_ci } 144cabdff1aSopenharmony_ci} 145cabdff1aSopenharmony_ci 146cabdff1aSopenharmony_cistatic void cdxl_decode_rgb(CDXLVideoContext *c, AVFrame *frame) 147cabdff1aSopenharmony_ci{ 148cabdff1aSopenharmony_ci uint32_t *new_palette = (uint32_t *)frame->data[1]; 149cabdff1aSopenharmony_ci 150cabdff1aSopenharmony_ci memset(frame->data[1], 0, AVPALETTE_SIZE); 151cabdff1aSopenharmony_ci import_palette(c, new_palette); 152cabdff1aSopenharmony_ci import_format(c, frame->linesize[0], frame->data[0]); 153cabdff1aSopenharmony_ci} 154cabdff1aSopenharmony_ci 155cabdff1aSopenharmony_cistatic void cdxl_decode_raw(CDXLVideoContext *c, AVFrame *frame) 156cabdff1aSopenharmony_ci{ 157cabdff1aSopenharmony_ci import_format(c, frame->linesize[0], frame->data[0]); 158cabdff1aSopenharmony_ci} 159cabdff1aSopenharmony_ci 160cabdff1aSopenharmony_cistatic void cdxl_decode_ham6(CDXLVideoContext *c, AVFrame *frame) 161cabdff1aSopenharmony_ci{ 162cabdff1aSopenharmony_ci AVCodecContext *avctx = c->avctx; 163cabdff1aSopenharmony_ci uint32_t new_palette[16], r, g, b; 164cabdff1aSopenharmony_ci uint8_t *ptr, *out, index, op; 165cabdff1aSopenharmony_ci int x, y; 166cabdff1aSopenharmony_ci 167cabdff1aSopenharmony_ci ptr = c->new_video; 168cabdff1aSopenharmony_ci out = frame->data[0]; 169cabdff1aSopenharmony_ci 170cabdff1aSopenharmony_ci import_palette(c, new_palette); 171cabdff1aSopenharmony_ci import_format(c, avctx->width, c->new_video); 172cabdff1aSopenharmony_ci 173cabdff1aSopenharmony_ci for (y = 0; y < avctx->height; y++) { 174cabdff1aSopenharmony_ci r = new_palette[0] & 0xFF0000; 175cabdff1aSopenharmony_ci g = new_palette[0] & 0xFF00; 176cabdff1aSopenharmony_ci b = new_palette[0] & 0xFF; 177cabdff1aSopenharmony_ci for (x = 0; x < avctx->width; x++) { 178cabdff1aSopenharmony_ci index = *ptr++; 179cabdff1aSopenharmony_ci op = index >> 4; 180cabdff1aSopenharmony_ci index &= 15; 181cabdff1aSopenharmony_ci switch (op) { 182cabdff1aSopenharmony_ci case 0: 183cabdff1aSopenharmony_ci r = new_palette[index] & 0xFF0000; 184cabdff1aSopenharmony_ci g = new_palette[index] & 0xFF00; 185cabdff1aSopenharmony_ci b = new_palette[index] & 0xFF; 186cabdff1aSopenharmony_ci break; 187cabdff1aSopenharmony_ci case 1: 188cabdff1aSopenharmony_ci b = index * 0x11; 189cabdff1aSopenharmony_ci break; 190cabdff1aSopenharmony_ci case 2: 191cabdff1aSopenharmony_ci r = index * 0x11 << 16; 192cabdff1aSopenharmony_ci break; 193cabdff1aSopenharmony_ci case 3: 194cabdff1aSopenharmony_ci g = index * 0x11 << 8; 195cabdff1aSopenharmony_ci break; 196cabdff1aSopenharmony_ci } 197cabdff1aSopenharmony_ci AV_WL24(out + x * 3, r | g | b); 198cabdff1aSopenharmony_ci } 199cabdff1aSopenharmony_ci out += frame->linesize[0]; 200cabdff1aSopenharmony_ci } 201cabdff1aSopenharmony_ci} 202cabdff1aSopenharmony_ci 203cabdff1aSopenharmony_cistatic void cdxl_decode_ham8(CDXLVideoContext *c, AVFrame *frame) 204cabdff1aSopenharmony_ci{ 205cabdff1aSopenharmony_ci AVCodecContext *avctx = c->avctx; 206cabdff1aSopenharmony_ci uint32_t new_palette[64], r, g, b; 207cabdff1aSopenharmony_ci uint8_t *ptr, *out, index, op; 208cabdff1aSopenharmony_ci int x, y; 209cabdff1aSopenharmony_ci 210cabdff1aSopenharmony_ci ptr = c->new_video; 211cabdff1aSopenharmony_ci out = frame->data[0]; 212cabdff1aSopenharmony_ci 213cabdff1aSopenharmony_ci import_palette(c, new_palette); 214cabdff1aSopenharmony_ci import_format(c, avctx->width, c->new_video); 215cabdff1aSopenharmony_ci 216cabdff1aSopenharmony_ci for (y = 0; y < avctx->height; y++) { 217cabdff1aSopenharmony_ci r = new_palette[0] & 0xFF0000; 218cabdff1aSopenharmony_ci g = new_palette[0] & 0xFF00; 219cabdff1aSopenharmony_ci b = new_palette[0] & 0xFF; 220cabdff1aSopenharmony_ci for (x = 0; x < avctx->width; x++) { 221cabdff1aSopenharmony_ci index = *ptr++; 222cabdff1aSopenharmony_ci op = index >> 6; 223cabdff1aSopenharmony_ci index &= 63; 224cabdff1aSopenharmony_ci switch (op) { 225cabdff1aSopenharmony_ci case 0: 226cabdff1aSopenharmony_ci r = new_palette[index] & 0xFF0000; 227cabdff1aSopenharmony_ci g = new_palette[index] & 0xFF00; 228cabdff1aSopenharmony_ci b = new_palette[index] & 0xFF; 229cabdff1aSopenharmony_ci break; 230cabdff1aSopenharmony_ci case 1: 231cabdff1aSopenharmony_ci b = (index << 2) | (b & 3); 232cabdff1aSopenharmony_ci break; 233cabdff1aSopenharmony_ci case 2: 234cabdff1aSopenharmony_ci r = (index << 18) | (r & (3 << 16)); 235cabdff1aSopenharmony_ci break; 236cabdff1aSopenharmony_ci case 3: 237cabdff1aSopenharmony_ci g = (index << 10) | (g & (3 << 8)); 238cabdff1aSopenharmony_ci break; 239cabdff1aSopenharmony_ci } 240cabdff1aSopenharmony_ci AV_WL24(out + x * 3, r | g | b); 241cabdff1aSopenharmony_ci } 242cabdff1aSopenharmony_ci out += frame->linesize[0]; 243cabdff1aSopenharmony_ci } 244cabdff1aSopenharmony_ci} 245cabdff1aSopenharmony_ci 246cabdff1aSopenharmony_cistatic int cdxl_decode_frame(AVCodecContext *avctx, AVFrame *p, 247cabdff1aSopenharmony_ci int *got_frame, AVPacket *pkt) 248cabdff1aSopenharmony_ci{ 249cabdff1aSopenharmony_ci CDXLVideoContext *c = avctx->priv_data; 250cabdff1aSopenharmony_ci int ret, w, h, encoding, aligned_width, buf_size = pkt->size; 251cabdff1aSopenharmony_ci const uint8_t *buf = pkt->data; 252cabdff1aSopenharmony_ci 253cabdff1aSopenharmony_ci if (buf_size < 32) 254cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 255cabdff1aSopenharmony_ci c->type = buf[0]; 256cabdff1aSopenharmony_ci encoding = buf[1] & 7; 257cabdff1aSopenharmony_ci c->format = buf[1] & 0xE0; 258cabdff1aSopenharmony_ci w = AV_RB16(&buf[14]); 259cabdff1aSopenharmony_ci h = AV_RB16(&buf[16]); 260cabdff1aSopenharmony_ci c->bpp = buf[19]; 261cabdff1aSopenharmony_ci c->palette_size = AV_RB16(&buf[20]); 262cabdff1aSopenharmony_ci c->palette = buf + 32; 263cabdff1aSopenharmony_ci c->video = c->palette + c->palette_size; 264cabdff1aSopenharmony_ci c->video_size = buf_size - c->palette_size - 32; 265cabdff1aSopenharmony_ci 266cabdff1aSopenharmony_ci if (c->type > 1) 267cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 268cabdff1aSopenharmony_ci if (c->type == 1 && c->palette_size > 512) 269cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 270cabdff1aSopenharmony_ci if (c->type == 0 && c->palette_size > 768) 271cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 272cabdff1aSopenharmony_ci if (buf_size < c->palette_size + 32) 273cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 274cabdff1aSopenharmony_ci if (c->bpp < 1) 275cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 276cabdff1aSopenharmony_ci if (c->format != BIT_PLANAR && c->format != BIT_LINE && c->format != CHUNKY) { 277cabdff1aSopenharmony_ci avpriv_request_sample(avctx, "Pixel format 0x%0x", c->format); 278cabdff1aSopenharmony_ci return AVERROR_PATCHWELCOME; 279cabdff1aSopenharmony_ci } 280cabdff1aSopenharmony_ci 281cabdff1aSopenharmony_ci if ((ret = ff_set_dimensions(avctx, w, h)) < 0) 282cabdff1aSopenharmony_ci return ret; 283cabdff1aSopenharmony_ci 284cabdff1aSopenharmony_ci if (c->format == CHUNKY) 285cabdff1aSopenharmony_ci aligned_width = avctx->width; 286cabdff1aSopenharmony_ci else 287cabdff1aSopenharmony_ci aligned_width = FFALIGN(c->avctx->width, 16); 288cabdff1aSopenharmony_ci c->padded_bits = aligned_width - c->avctx->width; 289cabdff1aSopenharmony_ci if (c->video_size < aligned_width * avctx->height * (int64_t)c->bpp / 8) 290cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 291cabdff1aSopenharmony_ci if (!encoding && c->palette_size && c->bpp <= 8 && c->format != CHUNKY) { 292cabdff1aSopenharmony_ci avctx->pix_fmt = AV_PIX_FMT_PAL8; 293cabdff1aSopenharmony_ci } else if (encoding == 1 && (c->bpp == 6 || c->bpp == 8) && c->format != CHUNKY) { 294cabdff1aSopenharmony_ci if (c->palette_size != (1 << (c->bpp - 1))) 295cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 296cabdff1aSopenharmony_ci avctx->pix_fmt = AV_PIX_FMT_BGR24; 297cabdff1aSopenharmony_ci } else if (!encoding && c->bpp == 24 && c->format == CHUNKY && 298cabdff1aSopenharmony_ci !c->palette_size) { 299cabdff1aSopenharmony_ci avctx->pix_fmt = AV_PIX_FMT_RGB24; 300cabdff1aSopenharmony_ci } else { 301cabdff1aSopenharmony_ci avpriv_request_sample(avctx, "Encoding %d, bpp %d and format 0x%x", 302cabdff1aSopenharmony_ci encoding, c->bpp, c->format); 303cabdff1aSopenharmony_ci return AVERROR_PATCHWELCOME; 304cabdff1aSopenharmony_ci } 305cabdff1aSopenharmony_ci 306cabdff1aSopenharmony_ci if ((ret = ff_get_buffer(avctx, p, 0)) < 0) 307cabdff1aSopenharmony_ci return ret; 308cabdff1aSopenharmony_ci p->pict_type = AV_PICTURE_TYPE_I; 309cabdff1aSopenharmony_ci p->key_frame = 1; 310cabdff1aSopenharmony_ci 311cabdff1aSopenharmony_ci if (encoding) { 312cabdff1aSopenharmony_ci av_fast_padded_malloc(&c->new_video, &c->new_video_size, 313cabdff1aSopenharmony_ci h * w + AV_INPUT_BUFFER_PADDING_SIZE); 314cabdff1aSopenharmony_ci if (!c->new_video) 315cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 316cabdff1aSopenharmony_ci if (c->bpp == 8) 317cabdff1aSopenharmony_ci cdxl_decode_ham8(c, p); 318cabdff1aSopenharmony_ci else 319cabdff1aSopenharmony_ci cdxl_decode_ham6(c, p); 320cabdff1aSopenharmony_ci } else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { 321cabdff1aSopenharmony_ci cdxl_decode_rgb(c, p); 322cabdff1aSopenharmony_ci } else { 323cabdff1aSopenharmony_ci cdxl_decode_raw(c, p); 324cabdff1aSopenharmony_ci } 325cabdff1aSopenharmony_ci *got_frame = 1; 326cabdff1aSopenharmony_ci 327cabdff1aSopenharmony_ci return buf_size; 328cabdff1aSopenharmony_ci} 329cabdff1aSopenharmony_ci 330cabdff1aSopenharmony_cistatic av_cold int cdxl_decode_end(AVCodecContext *avctx) 331cabdff1aSopenharmony_ci{ 332cabdff1aSopenharmony_ci CDXLVideoContext *c = avctx->priv_data; 333cabdff1aSopenharmony_ci 334cabdff1aSopenharmony_ci av_freep(&c->new_video); 335cabdff1aSopenharmony_ci 336cabdff1aSopenharmony_ci return 0; 337cabdff1aSopenharmony_ci} 338cabdff1aSopenharmony_ci 339cabdff1aSopenharmony_ciconst FFCodec ff_cdxl_decoder = { 340cabdff1aSopenharmony_ci .p.name = "cdxl", 341cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("Commodore CDXL video"), 342cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_VIDEO, 343cabdff1aSopenharmony_ci .p.id = AV_CODEC_ID_CDXL, 344cabdff1aSopenharmony_ci .priv_data_size = sizeof(CDXLVideoContext), 345cabdff1aSopenharmony_ci .init = cdxl_decode_init, 346cabdff1aSopenharmony_ci .close = cdxl_decode_end, 347cabdff1aSopenharmony_ci FF_CODEC_DECODE_CB(cdxl_decode_frame), 348cabdff1aSopenharmony_ci .p.capabilities = AV_CODEC_CAP_DR1, 349cabdff1aSopenharmony_ci .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 350cabdff1aSopenharmony_ci}; 351