1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * LPCM codecs for PCM format found in Blu-ray PCM streams 3cabdff1aSopenharmony_ci * Copyright (c) 2009, 2013 Christian Schmidt 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 * PCM codec for Blu-ray PCM audio tracks 25cabdff1aSopenharmony_ci */ 26cabdff1aSopenharmony_ci 27cabdff1aSopenharmony_ci#include "libavutil/channel_layout.h" 28cabdff1aSopenharmony_ci#include "avcodec.h" 29cabdff1aSopenharmony_ci#include "bytestream.h" 30cabdff1aSopenharmony_ci#include "codec_internal.h" 31cabdff1aSopenharmony_ci#include "internal.h" 32cabdff1aSopenharmony_ci 33cabdff1aSopenharmony_ci/* 34cabdff1aSopenharmony_ci * Channel Mapping according to 35cabdff1aSopenharmony_ci * Blu-ray Disc Read-Only Format Version 1 36cabdff1aSopenharmony_ci * Part 3: Audio Visual Basic Specifications 37cabdff1aSopenharmony_ci * mono M1 X 38cabdff1aSopenharmony_ci * stereo L R 39cabdff1aSopenharmony_ci * 3/0 L R C X 40cabdff1aSopenharmony_ci * 2/1 L R S X 41cabdff1aSopenharmony_ci * 3/1 L R C S 42cabdff1aSopenharmony_ci * 2/2 L R LS RS 43cabdff1aSopenharmony_ci * 3/2 L R C LS RS X 44cabdff1aSopenharmony_ci * 3/2+lfe L R C LS RS lfe 45cabdff1aSopenharmony_ci * 3/4 L R C LS Rls Rrs RS X 46cabdff1aSopenharmony_ci * 3/4+lfe L R C LS Rls Rrs RS lfe 47cabdff1aSopenharmony_ci */ 48cabdff1aSopenharmony_ci 49cabdff1aSopenharmony_ci/** 50cabdff1aSopenharmony_ci * Parse the header of a LPCM frame read from a Blu-ray MPEG-TS stream 51cabdff1aSopenharmony_ci * @param avctx the codec context 52cabdff1aSopenharmony_ci * @param header pointer to the first four bytes of the data packet 53cabdff1aSopenharmony_ci */ 54cabdff1aSopenharmony_cistatic int pcm_bluray_parse_header(AVCodecContext *avctx, 55cabdff1aSopenharmony_ci const uint8_t *header) 56cabdff1aSopenharmony_ci{ 57cabdff1aSopenharmony_ci static const uint8_t bits_per_samples[4] = { 0, 16, 20, 24 }; 58cabdff1aSopenharmony_ci static const AVChannelLayout channel_layouts[16] = { 59cabdff1aSopenharmony_ci { 0 }, AV_CHANNEL_LAYOUT_MONO, { 0 }, 60cabdff1aSopenharmony_ci AV_CHANNEL_LAYOUT_STEREO, AV_CHANNEL_LAYOUT_SURROUND, AV_CHANNEL_LAYOUT_2_1, 61cabdff1aSopenharmony_ci AV_CHANNEL_LAYOUT_4POINT0, AV_CHANNEL_LAYOUT_2_2, AV_CHANNEL_LAYOUT_5POINT0, 62cabdff1aSopenharmony_ci AV_CHANNEL_LAYOUT_5POINT1, AV_CHANNEL_LAYOUT_7POINT0, AV_CHANNEL_LAYOUT_7POINT1, 63cabdff1aSopenharmony_ci { 0 }, { 0 }, { 0 }, { 0 }, 64cabdff1aSopenharmony_ci }; 65cabdff1aSopenharmony_ci uint8_t channel_layout = header[2] >> 4; 66cabdff1aSopenharmony_ci 67cabdff1aSopenharmony_ci if (avctx->debug & FF_DEBUG_PICT_INFO) 68cabdff1aSopenharmony_ci ff_dlog(avctx, "pcm_bluray_parse_header: header = %02x%02x%02x%02x\n", 69cabdff1aSopenharmony_ci header[0], header[1], header[2], header[3]); 70cabdff1aSopenharmony_ci 71cabdff1aSopenharmony_ci /* get the sample depth and derive the sample format from it */ 72cabdff1aSopenharmony_ci avctx->bits_per_coded_sample = bits_per_samples[header[3] >> 6]; 73cabdff1aSopenharmony_ci if (!(avctx->bits_per_coded_sample == 16 || avctx->bits_per_coded_sample == 24)) { 74cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "unsupported sample depth (%d)\n", avctx->bits_per_coded_sample); 75cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 76cabdff1aSopenharmony_ci } 77cabdff1aSopenharmony_ci avctx->sample_fmt = avctx->bits_per_coded_sample == 16 ? AV_SAMPLE_FMT_S16 78cabdff1aSopenharmony_ci : AV_SAMPLE_FMT_S32; 79cabdff1aSopenharmony_ci if (avctx->sample_fmt == AV_SAMPLE_FMT_S32) 80cabdff1aSopenharmony_ci avctx->bits_per_raw_sample = avctx->bits_per_coded_sample; 81cabdff1aSopenharmony_ci 82cabdff1aSopenharmony_ci /* get the sample rate. Not all values are used. */ 83cabdff1aSopenharmony_ci switch (header[2] & 0x0f) { 84cabdff1aSopenharmony_ci case 1: 85cabdff1aSopenharmony_ci avctx->sample_rate = 48000; 86cabdff1aSopenharmony_ci break; 87cabdff1aSopenharmony_ci case 4: 88cabdff1aSopenharmony_ci avctx->sample_rate = 96000; 89cabdff1aSopenharmony_ci break; 90cabdff1aSopenharmony_ci case 5: 91cabdff1aSopenharmony_ci avctx->sample_rate = 192000; 92cabdff1aSopenharmony_ci break; 93cabdff1aSopenharmony_ci default: 94cabdff1aSopenharmony_ci avctx->sample_rate = 0; 95cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "reserved sample rate (%d)\n", 96cabdff1aSopenharmony_ci header[2] & 0x0f); 97cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 98cabdff1aSopenharmony_ci } 99cabdff1aSopenharmony_ci 100cabdff1aSopenharmony_ci /* 101cabdff1aSopenharmony_ci * get the channel number (and mapping). Not all values are used. 102cabdff1aSopenharmony_ci * It must be noted that the number of channels in the MPEG stream can 103cabdff1aSopenharmony_ci * differ from the actual meaningful number, e.g. mono audio still has two 104cabdff1aSopenharmony_ci * channels, one being empty. 105cabdff1aSopenharmony_ci */ 106cabdff1aSopenharmony_ci av_channel_layout_uninit(&avctx->ch_layout); 107cabdff1aSopenharmony_ci avctx->ch_layout = channel_layouts[channel_layout]; 108cabdff1aSopenharmony_ci if (!avctx->ch_layout.nb_channels) { 109cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "reserved channel configuration (%d)\n", 110cabdff1aSopenharmony_ci channel_layout); 111cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 112cabdff1aSopenharmony_ci } 113cabdff1aSopenharmony_ci 114cabdff1aSopenharmony_ci avctx->bit_rate = FFALIGN(avctx->ch_layout.nb_channels, 2) * avctx->sample_rate * 115cabdff1aSopenharmony_ci avctx->bits_per_coded_sample; 116cabdff1aSopenharmony_ci 117cabdff1aSopenharmony_ci if (avctx->debug & FF_DEBUG_PICT_INFO) 118cabdff1aSopenharmony_ci ff_dlog(avctx, 119cabdff1aSopenharmony_ci "pcm_bluray_parse_header: %d channels, %d bits per sample, %d Hz, %"PRId64" bit/s\n", 120cabdff1aSopenharmony_ci avctx->ch_layout.nb_channels, avctx->bits_per_coded_sample, 121cabdff1aSopenharmony_ci avctx->sample_rate, avctx->bit_rate); 122cabdff1aSopenharmony_ci return 0; 123cabdff1aSopenharmony_ci} 124cabdff1aSopenharmony_ci 125cabdff1aSopenharmony_cistatic int pcm_bluray_decode_frame(AVCodecContext *avctx, AVFrame *frame, 126cabdff1aSopenharmony_ci int *got_frame_ptr, AVPacket *avpkt) 127cabdff1aSopenharmony_ci{ 128cabdff1aSopenharmony_ci const uint8_t *src = avpkt->data; 129cabdff1aSopenharmony_ci int buf_size = avpkt->size; 130cabdff1aSopenharmony_ci GetByteContext gb; 131cabdff1aSopenharmony_ci int num_source_channels, channel, retval; 132cabdff1aSopenharmony_ci int sample_size, samples; 133cabdff1aSopenharmony_ci int16_t *dst16; 134cabdff1aSopenharmony_ci int32_t *dst32; 135cabdff1aSopenharmony_ci 136cabdff1aSopenharmony_ci if (buf_size < 4) { 137cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "PCM packet too small\n"); 138cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 139cabdff1aSopenharmony_ci } 140cabdff1aSopenharmony_ci 141cabdff1aSopenharmony_ci if ((retval = pcm_bluray_parse_header(avctx, src))) 142cabdff1aSopenharmony_ci return retval; 143cabdff1aSopenharmony_ci src += 4; 144cabdff1aSopenharmony_ci buf_size -= 4; 145cabdff1aSopenharmony_ci 146cabdff1aSopenharmony_ci bytestream2_init(&gb, src, buf_size); 147cabdff1aSopenharmony_ci 148cabdff1aSopenharmony_ci /* There's always an even number of channels in the source */ 149cabdff1aSopenharmony_ci num_source_channels = FFALIGN(avctx->ch_layout.nb_channels, 2); 150cabdff1aSopenharmony_ci sample_size = (num_source_channels * 151cabdff1aSopenharmony_ci (avctx->sample_fmt == AV_SAMPLE_FMT_S16 ? 16 : 24)) >> 3; 152cabdff1aSopenharmony_ci samples = buf_size / sample_size; 153cabdff1aSopenharmony_ci 154cabdff1aSopenharmony_ci /* get output buffer */ 155cabdff1aSopenharmony_ci frame->nb_samples = samples; 156cabdff1aSopenharmony_ci if ((retval = ff_get_buffer(avctx, frame, 0)) < 0) 157cabdff1aSopenharmony_ci return retval; 158cabdff1aSopenharmony_ci dst16 = (int16_t *)frame->data[0]; 159cabdff1aSopenharmony_ci dst32 = (int32_t *)frame->data[0]; 160cabdff1aSopenharmony_ci 161cabdff1aSopenharmony_ci if (samples) { 162cabdff1aSopenharmony_ci switch (avctx->ch_layout.u.mask) { 163cabdff1aSopenharmony_ci /* cases with same number of source and coded channels */ 164cabdff1aSopenharmony_ci case AV_CH_LAYOUT_STEREO: 165cabdff1aSopenharmony_ci case AV_CH_LAYOUT_4POINT0: 166cabdff1aSopenharmony_ci case AV_CH_LAYOUT_2_2: 167cabdff1aSopenharmony_ci samples *= num_source_channels; 168cabdff1aSopenharmony_ci if (AV_SAMPLE_FMT_S16 == avctx->sample_fmt) { 169cabdff1aSopenharmony_ci#if HAVE_BIGENDIAN 170cabdff1aSopenharmony_ci bytestream2_get_buffer(&gb, dst16, buf_size); 171cabdff1aSopenharmony_ci#else 172cabdff1aSopenharmony_ci do { 173cabdff1aSopenharmony_ci *dst16++ = bytestream2_get_be16u(&gb); 174cabdff1aSopenharmony_ci } while (--samples); 175cabdff1aSopenharmony_ci#endif 176cabdff1aSopenharmony_ci } else { 177cabdff1aSopenharmony_ci do { 178cabdff1aSopenharmony_ci *dst32++ = bytestream2_get_be24u(&gb) << 8; 179cabdff1aSopenharmony_ci } while (--samples); 180cabdff1aSopenharmony_ci } 181cabdff1aSopenharmony_ci break; 182cabdff1aSopenharmony_ci /* cases where number of source channels = coded channels + 1 */ 183cabdff1aSopenharmony_ci case AV_CH_LAYOUT_MONO: 184cabdff1aSopenharmony_ci case AV_CH_LAYOUT_SURROUND: 185cabdff1aSopenharmony_ci case AV_CH_LAYOUT_2_1: 186cabdff1aSopenharmony_ci case AV_CH_LAYOUT_5POINT0: 187cabdff1aSopenharmony_ci if (AV_SAMPLE_FMT_S16 == avctx->sample_fmt) { 188cabdff1aSopenharmony_ci do { 189cabdff1aSopenharmony_ci#if HAVE_BIGENDIAN 190cabdff1aSopenharmony_ci bytestream2_get_buffer(&gb, dst16, avctx->ch_layout.nb_channels * 2); 191cabdff1aSopenharmony_ci dst16 += avctx->ch_layout.nb_channels; 192cabdff1aSopenharmony_ci#else 193cabdff1aSopenharmony_ci channel = avctx->ch_layout.nb_channels; 194cabdff1aSopenharmony_ci do { 195cabdff1aSopenharmony_ci *dst16++ = bytestream2_get_be16u(&gb); 196cabdff1aSopenharmony_ci } while (--channel); 197cabdff1aSopenharmony_ci#endif 198cabdff1aSopenharmony_ci bytestream2_skip(&gb, 2); 199cabdff1aSopenharmony_ci } while (--samples); 200cabdff1aSopenharmony_ci } else { 201cabdff1aSopenharmony_ci do { 202cabdff1aSopenharmony_ci channel = avctx->ch_layout.nb_channels; 203cabdff1aSopenharmony_ci do { 204cabdff1aSopenharmony_ci *dst32++ = bytestream2_get_be24u(&gb) << 8; 205cabdff1aSopenharmony_ci } while (--channel); 206cabdff1aSopenharmony_ci bytestream2_skip(&gb, 3); 207cabdff1aSopenharmony_ci } while (--samples); 208cabdff1aSopenharmony_ci } 209cabdff1aSopenharmony_ci break; 210cabdff1aSopenharmony_ci /* remapping: L, R, C, LBack, RBack, LF */ 211cabdff1aSopenharmony_ci case AV_CH_LAYOUT_5POINT1: 212cabdff1aSopenharmony_ci if (AV_SAMPLE_FMT_S16 == avctx->sample_fmt) { 213cabdff1aSopenharmony_ci do { 214cabdff1aSopenharmony_ci dst16[0] = bytestream2_get_be16u(&gb); 215cabdff1aSopenharmony_ci dst16[1] = bytestream2_get_be16u(&gb); 216cabdff1aSopenharmony_ci dst16[2] = bytestream2_get_be16u(&gb); 217cabdff1aSopenharmony_ci dst16[4] = bytestream2_get_be16u(&gb); 218cabdff1aSopenharmony_ci dst16[5] = bytestream2_get_be16u(&gb); 219cabdff1aSopenharmony_ci dst16[3] = bytestream2_get_be16u(&gb); 220cabdff1aSopenharmony_ci dst16 += 6; 221cabdff1aSopenharmony_ci } while (--samples); 222cabdff1aSopenharmony_ci } else { 223cabdff1aSopenharmony_ci do { 224cabdff1aSopenharmony_ci dst32[0] = bytestream2_get_be24u(&gb) << 8; 225cabdff1aSopenharmony_ci dst32[1] = bytestream2_get_be24u(&gb) << 8; 226cabdff1aSopenharmony_ci dst32[2] = bytestream2_get_be24u(&gb) << 8; 227cabdff1aSopenharmony_ci dst32[4] = bytestream2_get_be24u(&gb) << 8; 228cabdff1aSopenharmony_ci dst32[5] = bytestream2_get_be24u(&gb) << 8; 229cabdff1aSopenharmony_ci dst32[3] = bytestream2_get_be24u(&gb) << 8; 230cabdff1aSopenharmony_ci dst32 += 6; 231cabdff1aSopenharmony_ci } while (--samples); 232cabdff1aSopenharmony_ci } 233cabdff1aSopenharmony_ci break; 234cabdff1aSopenharmony_ci /* remapping: L, R, C, LSide, LBack, RBack, RSide, <unused> */ 235cabdff1aSopenharmony_ci case AV_CH_LAYOUT_7POINT0: 236cabdff1aSopenharmony_ci if (AV_SAMPLE_FMT_S16 == avctx->sample_fmt) { 237cabdff1aSopenharmony_ci do { 238cabdff1aSopenharmony_ci dst16[0] = bytestream2_get_be16u(&gb); 239cabdff1aSopenharmony_ci dst16[1] = bytestream2_get_be16u(&gb); 240cabdff1aSopenharmony_ci dst16[2] = bytestream2_get_be16u(&gb); 241cabdff1aSopenharmony_ci dst16[5] = bytestream2_get_be16u(&gb); 242cabdff1aSopenharmony_ci dst16[3] = bytestream2_get_be16u(&gb); 243cabdff1aSopenharmony_ci dst16[4] = bytestream2_get_be16u(&gb); 244cabdff1aSopenharmony_ci dst16[6] = bytestream2_get_be16u(&gb); 245cabdff1aSopenharmony_ci dst16 += 7; 246cabdff1aSopenharmony_ci bytestream2_skip(&gb, 2); 247cabdff1aSopenharmony_ci } while (--samples); 248cabdff1aSopenharmony_ci } else { 249cabdff1aSopenharmony_ci do { 250cabdff1aSopenharmony_ci dst32[0] = bytestream2_get_be24u(&gb) << 8; 251cabdff1aSopenharmony_ci dst32[1] = bytestream2_get_be24u(&gb) << 8; 252cabdff1aSopenharmony_ci dst32[2] = bytestream2_get_be24u(&gb) << 8; 253cabdff1aSopenharmony_ci dst32[5] = bytestream2_get_be24u(&gb) << 8; 254cabdff1aSopenharmony_ci dst32[3] = bytestream2_get_be24u(&gb) << 8; 255cabdff1aSopenharmony_ci dst32[4] = bytestream2_get_be24u(&gb) << 8; 256cabdff1aSopenharmony_ci dst32[6] = bytestream2_get_be24u(&gb) << 8; 257cabdff1aSopenharmony_ci dst32 += 7; 258cabdff1aSopenharmony_ci bytestream2_skip(&gb, 3); 259cabdff1aSopenharmony_ci } while (--samples); 260cabdff1aSopenharmony_ci } 261cabdff1aSopenharmony_ci break; 262cabdff1aSopenharmony_ci /* remapping: L, R, C, LSide, LBack, RBack, RSide, LF */ 263cabdff1aSopenharmony_ci case AV_CH_LAYOUT_7POINT1: 264cabdff1aSopenharmony_ci if (AV_SAMPLE_FMT_S16 == avctx->sample_fmt) { 265cabdff1aSopenharmony_ci do { 266cabdff1aSopenharmony_ci dst16[0] = bytestream2_get_be16u(&gb); 267cabdff1aSopenharmony_ci dst16[1] = bytestream2_get_be16u(&gb); 268cabdff1aSopenharmony_ci dst16[2] = bytestream2_get_be16u(&gb); 269cabdff1aSopenharmony_ci dst16[6] = bytestream2_get_be16u(&gb); 270cabdff1aSopenharmony_ci dst16[4] = bytestream2_get_be16u(&gb); 271cabdff1aSopenharmony_ci dst16[5] = bytestream2_get_be16u(&gb); 272cabdff1aSopenharmony_ci dst16[7] = bytestream2_get_be16u(&gb); 273cabdff1aSopenharmony_ci dst16[3] = bytestream2_get_be16u(&gb); 274cabdff1aSopenharmony_ci dst16 += 8; 275cabdff1aSopenharmony_ci } while (--samples); 276cabdff1aSopenharmony_ci } else { 277cabdff1aSopenharmony_ci do { 278cabdff1aSopenharmony_ci dst32[0] = bytestream2_get_be24u(&gb) << 8; 279cabdff1aSopenharmony_ci dst32[1] = bytestream2_get_be24u(&gb) << 8; 280cabdff1aSopenharmony_ci dst32[2] = bytestream2_get_be24u(&gb) << 8; 281cabdff1aSopenharmony_ci dst32[6] = bytestream2_get_be24u(&gb) << 8; 282cabdff1aSopenharmony_ci dst32[4] = bytestream2_get_be24u(&gb) << 8; 283cabdff1aSopenharmony_ci dst32[5] = bytestream2_get_be24u(&gb) << 8; 284cabdff1aSopenharmony_ci dst32[7] = bytestream2_get_be24u(&gb) << 8; 285cabdff1aSopenharmony_ci dst32[3] = bytestream2_get_be24u(&gb) << 8; 286cabdff1aSopenharmony_ci dst32 += 8; 287cabdff1aSopenharmony_ci } while (--samples); 288cabdff1aSopenharmony_ci } 289cabdff1aSopenharmony_ci break; 290cabdff1aSopenharmony_ci } 291cabdff1aSopenharmony_ci } 292cabdff1aSopenharmony_ci 293cabdff1aSopenharmony_ci *got_frame_ptr = 1; 294cabdff1aSopenharmony_ci 295cabdff1aSopenharmony_ci retval = bytestream2_tell(&gb); 296cabdff1aSopenharmony_ci if (avctx->debug & FF_DEBUG_BITSTREAM) 297cabdff1aSopenharmony_ci ff_dlog(avctx, "pcm_bluray_decode_frame: decoded %d -> %d bytes\n", 298cabdff1aSopenharmony_ci retval, buf_size); 299cabdff1aSopenharmony_ci return retval + 4; 300cabdff1aSopenharmony_ci} 301cabdff1aSopenharmony_ci 302cabdff1aSopenharmony_ciconst FFCodec ff_pcm_bluray_decoder = { 303cabdff1aSopenharmony_ci .p.name = "pcm_bluray", 304cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("PCM signed 16|20|24-bit big-endian for Blu-ray media"), 305cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_AUDIO, 306cabdff1aSopenharmony_ci .p.id = AV_CODEC_ID_PCM_BLURAY, 307cabdff1aSopenharmony_ci FF_CODEC_DECODE_CB(pcm_bluray_decode_frame), 308cabdff1aSopenharmony_ci .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, 309cabdff1aSopenharmony_ci .p.sample_fmts = (const enum AVSampleFormat[]){ 310cabdff1aSopenharmony_ci AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_NONE 311cabdff1aSopenharmony_ci }, 312cabdff1aSopenharmony_ci}; 313