1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * APNG muxer 3cabdff1aSopenharmony_ci * Copyright (c) 2015 Donny Yang 4cabdff1aSopenharmony_ci * 5cabdff1aSopenharmony_ci * first version by Donny Yang <work@kota.moe> 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * This file is part of FFmpeg. 8cabdff1aSopenharmony_ci * 9cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 10cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 11cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 12cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 13cabdff1aSopenharmony_ci * 14cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 15cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 16cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17cabdff1aSopenharmony_ci * Lesser General Public License for more details. 18cabdff1aSopenharmony_ci * 19cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 20cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 21cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22cabdff1aSopenharmony_ci */ 23cabdff1aSopenharmony_ci 24cabdff1aSopenharmony_ci#include "avformat.h" 25cabdff1aSopenharmony_ci#include "libavutil/avassert.h" 26cabdff1aSopenharmony_ci#include "libavutil/crc.h" 27cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h" 28cabdff1aSopenharmony_ci#include "libavutil/log.h" 29cabdff1aSopenharmony_ci#include "libavutil/opt.h" 30cabdff1aSopenharmony_ci#include "libavcodec/apng.h" 31cabdff1aSopenharmony_ci#include "libavcodec/png.h" 32cabdff1aSopenharmony_ci 33cabdff1aSopenharmony_citypedef struct APNGMuxContext { 34cabdff1aSopenharmony_ci AVClass *class; 35cabdff1aSopenharmony_ci 36cabdff1aSopenharmony_ci uint32_t plays; 37cabdff1aSopenharmony_ci AVRational last_delay; 38cabdff1aSopenharmony_ci 39cabdff1aSopenharmony_ci uint64_t acTL_offset; 40cabdff1aSopenharmony_ci uint32_t frame_number; 41cabdff1aSopenharmony_ci 42cabdff1aSopenharmony_ci AVPacket *prev_packet; 43cabdff1aSopenharmony_ci AVRational prev_delay; 44cabdff1aSopenharmony_ci 45cabdff1aSopenharmony_ci int framerate_warned; 46cabdff1aSopenharmony_ci 47cabdff1aSopenharmony_ci uint8_t *extra_data; 48cabdff1aSopenharmony_ci int extra_data_size; 49cabdff1aSopenharmony_ci} APNGMuxContext; 50cabdff1aSopenharmony_ci 51cabdff1aSopenharmony_cistatic const uint8_t *apng_find_chunk(uint32_t tag, const uint8_t *buf, 52cabdff1aSopenharmony_ci size_t length) 53cabdff1aSopenharmony_ci{ 54cabdff1aSopenharmony_ci size_t b; 55cabdff1aSopenharmony_ci for (b = 0; AV_RB32(buf + b) + 12ULL <= length - b; b += AV_RB32(buf + b) + 12ULL) 56cabdff1aSopenharmony_ci if (AV_RB32(&buf[b + 4]) == tag) 57cabdff1aSopenharmony_ci return &buf[b]; 58cabdff1aSopenharmony_ci return NULL; 59cabdff1aSopenharmony_ci} 60cabdff1aSopenharmony_ci 61cabdff1aSopenharmony_cistatic void apng_write_chunk(AVIOContext *io_context, uint32_t tag, 62cabdff1aSopenharmony_ci uint8_t *buf, size_t length) 63cabdff1aSopenharmony_ci{ 64cabdff1aSopenharmony_ci const AVCRC *crc_table = av_crc_get_table(AV_CRC_32_IEEE_LE); 65cabdff1aSopenharmony_ci uint32_t crc = ~0U; 66cabdff1aSopenharmony_ci uint8_t tagbuf[4]; 67cabdff1aSopenharmony_ci 68cabdff1aSopenharmony_ci av_assert0(crc_table); 69cabdff1aSopenharmony_ci 70cabdff1aSopenharmony_ci avio_wb32(io_context, length); 71cabdff1aSopenharmony_ci AV_WB32(tagbuf, tag); 72cabdff1aSopenharmony_ci crc = av_crc(crc_table, crc, tagbuf, 4); 73cabdff1aSopenharmony_ci avio_wb32(io_context, tag); 74cabdff1aSopenharmony_ci if (length > 0) { 75cabdff1aSopenharmony_ci crc = av_crc(crc_table, crc, buf, length); 76cabdff1aSopenharmony_ci avio_write(io_context, buf, length); 77cabdff1aSopenharmony_ci } 78cabdff1aSopenharmony_ci avio_wb32(io_context, ~crc); 79cabdff1aSopenharmony_ci} 80cabdff1aSopenharmony_ci 81cabdff1aSopenharmony_cistatic int apng_write_header(AVFormatContext *format_context) 82cabdff1aSopenharmony_ci{ 83cabdff1aSopenharmony_ci APNGMuxContext *apng = format_context->priv_data; 84cabdff1aSopenharmony_ci AVCodecParameters *par = format_context->streams[0]->codecpar; 85cabdff1aSopenharmony_ci 86cabdff1aSopenharmony_ci if (format_context->nb_streams != 1 || 87cabdff1aSopenharmony_ci format_context->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO || 88cabdff1aSopenharmony_ci format_context->streams[0]->codecpar->codec_id != AV_CODEC_ID_APNG) { 89cabdff1aSopenharmony_ci av_log(format_context, AV_LOG_ERROR, 90cabdff1aSopenharmony_ci "APNG muxer supports only a single video APNG stream.\n"); 91cabdff1aSopenharmony_ci return AVERROR(EINVAL); 92cabdff1aSopenharmony_ci } 93cabdff1aSopenharmony_ci 94cabdff1aSopenharmony_ci if (apng->last_delay.num > UINT16_MAX || apng->last_delay.den > UINT16_MAX) { 95cabdff1aSopenharmony_ci av_reduce(&apng->last_delay.num, &apng->last_delay.den, 96cabdff1aSopenharmony_ci apng->last_delay.num, apng->last_delay.den, UINT16_MAX); 97cabdff1aSopenharmony_ci av_log(format_context, AV_LOG_WARNING, 98cabdff1aSopenharmony_ci "Last frame delay is too precise. Reducing to %d/%d (%f).\n", 99cabdff1aSopenharmony_ci apng->last_delay.num, apng->last_delay.den, (double)apng->last_delay.num / apng->last_delay.den); 100cabdff1aSopenharmony_ci } 101cabdff1aSopenharmony_ci 102cabdff1aSopenharmony_ci avio_wb64(format_context->pb, PNGSIG); 103cabdff1aSopenharmony_ci // Remaining headers are written when they are copied from the encoder 104cabdff1aSopenharmony_ci 105cabdff1aSopenharmony_ci if (par->extradata_size) { 106cabdff1aSopenharmony_ci apng->extra_data = av_mallocz(par->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); 107cabdff1aSopenharmony_ci if (!apng->extra_data) 108cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 109cabdff1aSopenharmony_ci apng->extra_data_size = par->extradata_size; 110cabdff1aSopenharmony_ci memcpy(apng->extra_data, par->extradata, par->extradata_size); 111cabdff1aSopenharmony_ci } 112cabdff1aSopenharmony_ci 113cabdff1aSopenharmony_ci return 0; 114cabdff1aSopenharmony_ci} 115cabdff1aSopenharmony_ci 116cabdff1aSopenharmony_cistatic int flush_packet(AVFormatContext *format_context, AVPacket *packet) 117cabdff1aSopenharmony_ci{ 118cabdff1aSopenharmony_ci APNGMuxContext *apng = format_context->priv_data; 119cabdff1aSopenharmony_ci AVIOContext *io_context = format_context->pb; 120cabdff1aSopenharmony_ci AVStream *codec_stream = format_context->streams[0]; 121cabdff1aSopenharmony_ci uint8_t *side_data = NULL; 122cabdff1aSopenharmony_ci size_t side_data_size; 123cabdff1aSopenharmony_ci 124cabdff1aSopenharmony_ci av_assert0(apng->prev_packet); 125cabdff1aSopenharmony_ci 126cabdff1aSopenharmony_ci side_data = av_packet_get_side_data(apng->prev_packet, AV_PKT_DATA_NEW_EXTRADATA, &side_data_size); 127cabdff1aSopenharmony_ci 128cabdff1aSopenharmony_ci if (side_data_size) { 129cabdff1aSopenharmony_ci av_freep(&apng->extra_data); 130cabdff1aSopenharmony_ci apng->extra_data = av_mallocz(side_data_size + AV_INPUT_BUFFER_PADDING_SIZE); 131cabdff1aSopenharmony_ci if (!apng->extra_data) 132cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 133cabdff1aSopenharmony_ci apng->extra_data_size = side_data_size; 134cabdff1aSopenharmony_ci memcpy(apng->extra_data, side_data, apng->extra_data_size); 135cabdff1aSopenharmony_ci } 136cabdff1aSopenharmony_ci 137cabdff1aSopenharmony_ci if (apng->frame_number == 0 && !packet) { 138cabdff1aSopenharmony_ci const uint8_t *existing_acTL_chunk; 139cabdff1aSopenharmony_ci const uint8_t *existing_fcTL_chunk; 140cabdff1aSopenharmony_ci 141cabdff1aSopenharmony_ci av_log(format_context, AV_LOG_INFO, "Only a single frame so saving as a normal PNG.\n"); 142cabdff1aSopenharmony_ci 143cabdff1aSopenharmony_ci // Write normal PNG headers without acTL chunk 144cabdff1aSopenharmony_ci existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), apng->extra_data, apng->extra_data_size); 145cabdff1aSopenharmony_ci if (existing_acTL_chunk) { 146cabdff1aSopenharmony_ci const uint8_t *chunk_after_acTL = existing_acTL_chunk + AV_RB32(existing_acTL_chunk) + 12; 147cabdff1aSopenharmony_ci avio_write(io_context, apng->extra_data, existing_acTL_chunk - apng->extra_data); 148cabdff1aSopenharmony_ci avio_write(io_context, chunk_after_acTL, apng->extra_data + apng->extra_data_size - chunk_after_acTL); 149cabdff1aSopenharmony_ci } else { 150cabdff1aSopenharmony_ci avio_write(io_context, apng->extra_data, apng->extra_data_size); 151cabdff1aSopenharmony_ci } 152cabdff1aSopenharmony_ci 153cabdff1aSopenharmony_ci // Write frame data without fcTL chunk 154cabdff1aSopenharmony_ci existing_fcTL_chunk = apng_find_chunk(MKBETAG('f', 'c', 'T', 'L'), apng->prev_packet->data, apng->prev_packet->size); 155cabdff1aSopenharmony_ci if (existing_fcTL_chunk) { 156cabdff1aSopenharmony_ci const uint8_t *chunk_after_fcTL = existing_fcTL_chunk + AV_RB32(existing_fcTL_chunk) + 12; 157cabdff1aSopenharmony_ci avio_write(io_context, apng->prev_packet->data, existing_fcTL_chunk - apng->prev_packet->data); 158cabdff1aSopenharmony_ci avio_write(io_context, chunk_after_fcTL, apng->prev_packet->data + apng->prev_packet->size - chunk_after_fcTL); 159cabdff1aSopenharmony_ci } else { 160cabdff1aSopenharmony_ci avio_write(io_context, apng->prev_packet->data, apng->prev_packet->size); 161cabdff1aSopenharmony_ci } 162cabdff1aSopenharmony_ci } else { 163cabdff1aSopenharmony_ci const uint8_t *data, *data_end; 164cabdff1aSopenharmony_ci const uint8_t *existing_fcTL_chunk; 165cabdff1aSopenharmony_ci 166cabdff1aSopenharmony_ci if (apng->frame_number == 0) { 167cabdff1aSopenharmony_ci const uint8_t *existing_acTL_chunk; 168cabdff1aSopenharmony_ci 169cabdff1aSopenharmony_ci // Write normal PNG headers 170cabdff1aSopenharmony_ci avio_write(io_context, apng->extra_data, apng->extra_data_size); 171cabdff1aSopenharmony_ci 172cabdff1aSopenharmony_ci existing_acTL_chunk = apng_find_chunk(MKBETAG('a', 'c', 'T', 'L'), apng->extra_data, apng->extra_data_size); 173cabdff1aSopenharmony_ci if (!existing_acTL_chunk) { 174cabdff1aSopenharmony_ci uint8_t buf[8]; 175cabdff1aSopenharmony_ci // Write animation control header 176cabdff1aSopenharmony_ci apng->acTL_offset = avio_tell(io_context); 177cabdff1aSopenharmony_ci AV_WB32(buf, UINT_MAX); // number of frames (filled in later) 178cabdff1aSopenharmony_ci AV_WB32(buf + 4, apng->plays); 179cabdff1aSopenharmony_ci apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8); 180cabdff1aSopenharmony_ci } 181cabdff1aSopenharmony_ci } 182cabdff1aSopenharmony_ci 183cabdff1aSopenharmony_ci data = apng->prev_packet->data; 184cabdff1aSopenharmony_ci data_end = data + apng->prev_packet->size; 185cabdff1aSopenharmony_ci existing_fcTL_chunk = apng_find_chunk(MKBETAG('f', 'c', 'T', 'L'), apng->prev_packet->data, apng->prev_packet->size); 186cabdff1aSopenharmony_ci if (existing_fcTL_chunk) { 187cabdff1aSopenharmony_ci AVRational delay; 188cabdff1aSopenharmony_ci 189cabdff1aSopenharmony_ci if (AV_RB32(existing_fcTL_chunk) != APNG_FCTL_CHUNK_SIZE) 190cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 191cabdff1aSopenharmony_ci 192cabdff1aSopenharmony_ci existing_fcTL_chunk += 8; 193cabdff1aSopenharmony_ci delay.num = AV_RB16(existing_fcTL_chunk + 20); 194cabdff1aSopenharmony_ci delay.den = AV_RB16(existing_fcTL_chunk + 22); 195cabdff1aSopenharmony_ci 196cabdff1aSopenharmony_ci if (delay.num == 0 && delay.den == 0) { 197cabdff1aSopenharmony_ci uint8_t new_fcTL_chunk[APNG_FCTL_CHUNK_SIZE]; 198cabdff1aSopenharmony_ci 199cabdff1aSopenharmony_ci if (packet) { 200cabdff1aSopenharmony_ci int64_t delay_num_raw = (packet->dts - apng->prev_packet->dts) * codec_stream->time_base.num; 201cabdff1aSopenharmony_ci int64_t delay_den_raw = codec_stream->time_base.den; 202cabdff1aSopenharmony_ci if (!av_reduce(&delay.num, &delay.den, delay_num_raw, delay_den_raw, UINT16_MAX) && 203cabdff1aSopenharmony_ci !apng->framerate_warned) { 204cabdff1aSopenharmony_ci av_log(format_context, AV_LOG_WARNING, 205cabdff1aSopenharmony_ci "Frame rate is too high or specified too precisely. Unable to copy losslessly.\n"); 206cabdff1aSopenharmony_ci apng->framerate_warned = 1; 207cabdff1aSopenharmony_ci } 208cabdff1aSopenharmony_ci } else if (apng->last_delay.num > 0) { 209cabdff1aSopenharmony_ci delay = apng->last_delay; 210cabdff1aSopenharmony_ci } else { 211cabdff1aSopenharmony_ci delay = apng->prev_delay; 212cabdff1aSopenharmony_ci } 213cabdff1aSopenharmony_ci 214cabdff1aSopenharmony_ci avio_write(io_context, data, (existing_fcTL_chunk - 8) - data); 215cabdff1aSopenharmony_ci data = existing_fcTL_chunk + APNG_FCTL_CHUNK_SIZE + 4 /* CRC-32 */; 216cabdff1aSopenharmony_ci // Update frame control header with new delay 217cabdff1aSopenharmony_ci memcpy(new_fcTL_chunk, existing_fcTL_chunk, sizeof(new_fcTL_chunk)); 218cabdff1aSopenharmony_ci AV_WB16(new_fcTL_chunk + 20, delay.num); 219cabdff1aSopenharmony_ci AV_WB16(new_fcTL_chunk + 22, delay.den); 220cabdff1aSopenharmony_ci apng_write_chunk(io_context, MKBETAG('f', 'c', 'T', 'L'), 221cabdff1aSopenharmony_ci new_fcTL_chunk, sizeof(new_fcTL_chunk)); 222cabdff1aSopenharmony_ci } 223cabdff1aSopenharmony_ci apng->prev_delay = delay; 224cabdff1aSopenharmony_ci } 225cabdff1aSopenharmony_ci 226cabdff1aSopenharmony_ci // Write frame data 227cabdff1aSopenharmony_ci avio_write(io_context, data, data_end - data); 228cabdff1aSopenharmony_ci } 229cabdff1aSopenharmony_ci ++apng->frame_number; 230cabdff1aSopenharmony_ci 231cabdff1aSopenharmony_ci av_packet_unref(apng->prev_packet); 232cabdff1aSopenharmony_ci if (packet) 233cabdff1aSopenharmony_ci av_packet_ref(apng->prev_packet, packet); 234cabdff1aSopenharmony_ci return 0; 235cabdff1aSopenharmony_ci} 236cabdff1aSopenharmony_ci 237cabdff1aSopenharmony_cistatic int apng_write_packet(AVFormatContext *format_context, AVPacket *packet) 238cabdff1aSopenharmony_ci{ 239cabdff1aSopenharmony_ci APNGMuxContext *apng = format_context->priv_data; 240cabdff1aSopenharmony_ci int ret; 241cabdff1aSopenharmony_ci 242cabdff1aSopenharmony_ci if (!apng->prev_packet) { 243cabdff1aSopenharmony_ci apng->prev_packet = av_packet_alloc(); 244cabdff1aSopenharmony_ci if (!apng->prev_packet) 245cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 246cabdff1aSopenharmony_ci 247cabdff1aSopenharmony_ci av_packet_ref(apng->prev_packet, packet); 248cabdff1aSopenharmony_ci } else { 249cabdff1aSopenharmony_ci ret = flush_packet(format_context, packet); 250cabdff1aSopenharmony_ci if (ret < 0) 251cabdff1aSopenharmony_ci return ret; 252cabdff1aSopenharmony_ci } 253cabdff1aSopenharmony_ci 254cabdff1aSopenharmony_ci return 0; 255cabdff1aSopenharmony_ci} 256cabdff1aSopenharmony_ci 257cabdff1aSopenharmony_cistatic int apng_write_trailer(AVFormatContext *format_context) 258cabdff1aSopenharmony_ci{ 259cabdff1aSopenharmony_ci APNGMuxContext *apng = format_context->priv_data; 260cabdff1aSopenharmony_ci AVIOContext *io_context = format_context->pb; 261cabdff1aSopenharmony_ci uint8_t buf[8]; 262cabdff1aSopenharmony_ci int ret; 263cabdff1aSopenharmony_ci 264cabdff1aSopenharmony_ci if (apng->prev_packet) { 265cabdff1aSopenharmony_ci ret = flush_packet(format_context, NULL); 266cabdff1aSopenharmony_ci if (ret < 0) 267cabdff1aSopenharmony_ci return ret; 268cabdff1aSopenharmony_ci } 269cabdff1aSopenharmony_ci 270cabdff1aSopenharmony_ci apng_write_chunk(io_context, MKBETAG('I', 'E', 'N', 'D'), NULL, 0); 271cabdff1aSopenharmony_ci 272cabdff1aSopenharmony_ci if (apng->acTL_offset && (io_context->seekable & AVIO_SEEKABLE_NORMAL)) { 273cabdff1aSopenharmony_ci avio_seek(io_context, apng->acTL_offset, SEEK_SET); 274cabdff1aSopenharmony_ci 275cabdff1aSopenharmony_ci AV_WB32(buf, apng->frame_number); 276cabdff1aSopenharmony_ci AV_WB32(buf + 4, apng->plays); 277cabdff1aSopenharmony_ci apng_write_chunk(io_context, MKBETAG('a', 'c', 'T', 'L'), buf, 8); 278cabdff1aSopenharmony_ci } 279cabdff1aSopenharmony_ci 280cabdff1aSopenharmony_ci return 0; 281cabdff1aSopenharmony_ci} 282cabdff1aSopenharmony_ci 283cabdff1aSopenharmony_cistatic void apng_deinit(AVFormatContext *s) 284cabdff1aSopenharmony_ci{ 285cabdff1aSopenharmony_ci APNGMuxContext *apng = s->priv_data; 286cabdff1aSopenharmony_ci 287cabdff1aSopenharmony_ci av_packet_free(&apng->prev_packet); 288cabdff1aSopenharmony_ci av_freep(&apng->extra_data); 289cabdff1aSopenharmony_ci apng->extra_data_size = 0; 290cabdff1aSopenharmony_ci} 291cabdff1aSopenharmony_ci 292cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(APNGMuxContext, x) 293cabdff1aSopenharmony_ci#define ENC AV_OPT_FLAG_ENCODING_PARAM 294cabdff1aSopenharmony_cistatic const AVOption options[] = { 295cabdff1aSopenharmony_ci { "plays", "Number of times to play the output: 0 - infinite loop, 1 - no loop", OFFSET(plays), 296cabdff1aSopenharmony_ci AV_OPT_TYPE_INT, { .i64 = 1 }, 0, UINT16_MAX, ENC }, 297cabdff1aSopenharmony_ci { "final_delay", "Force delay after the last frame", OFFSET(last_delay), 298cabdff1aSopenharmony_ci AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, UINT16_MAX, ENC }, 299cabdff1aSopenharmony_ci { NULL }, 300cabdff1aSopenharmony_ci}; 301cabdff1aSopenharmony_ci 302cabdff1aSopenharmony_cistatic const AVClass apng_muxer_class = { 303cabdff1aSopenharmony_ci .class_name = "APNG muxer", 304cabdff1aSopenharmony_ci .item_name = av_default_item_name, 305cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 306cabdff1aSopenharmony_ci .option = options, 307cabdff1aSopenharmony_ci}; 308cabdff1aSopenharmony_ci 309cabdff1aSopenharmony_ciconst AVOutputFormat ff_apng_muxer = { 310cabdff1aSopenharmony_ci .name = "apng", 311cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"), 312cabdff1aSopenharmony_ci .mime_type = "image/png", 313cabdff1aSopenharmony_ci .extensions = "apng", 314cabdff1aSopenharmony_ci .priv_data_size = sizeof(APNGMuxContext), 315cabdff1aSopenharmony_ci .audio_codec = AV_CODEC_ID_NONE, 316cabdff1aSopenharmony_ci .video_codec = AV_CODEC_ID_APNG, 317cabdff1aSopenharmony_ci .write_header = apng_write_header, 318cabdff1aSopenharmony_ci .write_packet = apng_write_packet, 319cabdff1aSopenharmony_ci .write_trailer = apng_write_trailer, 320cabdff1aSopenharmony_ci .deinit = apng_deinit, 321cabdff1aSopenharmony_ci .priv_class = &apng_muxer_class, 322cabdff1aSopenharmony_ci .flags = AVFMT_VARIABLE_FPS, 323cabdff1aSopenharmony_ci}; 324