From: "Dawid Kozinski/Multimedia \(PLT\) /SRPOL/Staff Engineer/Samsung Electronics" <d.kozinski@samsung.com> To: "'FFmpeg development discussions and patches'" <ffmpeg-devel@ffmpeg.org> Subject: Re: [FFmpeg-devel] [PATCH v18 02/10] avcodec/evc_parser: Added parser implementation for EVC format Date: Wed, 5 Apr 2023 10:59:48 +0200 Message-ID: <000001d9679c$fa09ddd0$ee1d9970$@samsung.com> (raw) In-Reply-To: <e60b1bb7-d88a-627e-910e-6be1f039eeb1@gmail.com> > -----Original Message----- > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of James > Almer > Sent: środa, 29 marca 2023 16:57 > To: ffmpeg-devel@ffmpeg.org > Subject: Re: [FFmpeg-devel] [PATCH v18 02/10] avcodec/evc_parser: Added > parser implementation for EVC format > > > > On 3/28/2023 10:46 AM, Dawid Kozinski wrote: > > - Added constants definitions for EVC parser > > - Provided NAL units parsing following ISO_IEC_23094-1 > > - EVC parser registration > > > > Signed-off-by: Dawid Kozinski <d.kozinski@samsung.com> > > --- > > libavcodec/Makefile | 1 + > > libavcodec/evc.h | 155 +++++ > > libavcodec/evc_parser.c | 1270 > +++++++++++++++++++++++++++++++++++++++ > > libavcodec/parsers.c | 1 + > > 4 files changed, 1427 insertions(+) > > create mode 100644 libavcodec/evc.h > > create mode 100644 libavcodec/evc_parser.c > > > > [...] > > > +static int parse_nal_unit(AVCodecParserContext *s, const uint8_t *buf, > > + int buf_size, AVCodecContext *avctx) { > > + EVCParserContext *ev = s->priv_data; > > + int nalu_type, nalu_size; > > + int tid; > > + const uint8_t *data = buf; > > + int data_size = buf_size; > > + > > + s->picture_structure = AV_PICTURE_STRUCTURE_FRAME; > > + s->key_frame = -1; > > + > > + > > + nalu_size = buf_size; > > + if (nalu_size <= 0) { > > + av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit size: (%d)\n", nalu_size); > > + return AVERROR_INVALIDDATA; > > + } > > + > > + // @see ISO_IEC_23094-1_2020, 7.4.2.2 NAL unit header semantic (Table 4 > - NAL unit type codes and NAL unit type classes) > > + // @see enum EVCNALUnitType in evc.h > > + nalu_type = get_nalu_type(data, data_size, avctx); > > + if (nalu_type < EVC_NOIDR_NUT || nalu_type > EVC_UNSPEC_NUT62) { > > + av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit type: (%d)\n", > nalu_type); > > + return AVERROR_INVALIDDATA; > > + } > > + ev->nalu_type = nalu_type; > > + > > + tid = get_temporal_id(data, data_size, avctx); > > + if (tid < 0) { > > + av_log(avctx, AV_LOG_ERROR, "Invalid temporial id: (%d)\n", tid); > > + return AVERROR_INVALIDDATA; > > + } > > + ev->nuh_temporal_id = tid; > > + > > + if (data_size < nalu_size) { > > + av_log(avctx, AV_LOG_ERROR, "NAL unit does not fit in the data > buffer\n"); > > + return AVERROR_INVALIDDATA; > > + } > > + > > + data += EVC_NALU_HEADER_SIZE; > > + data_size -= EVC_NALU_HEADER_SIZE; > > + > > + if (nalu_type == EVC_SPS_NUT) { > > + EVCParserSPS *sps; > > + int SubGopLength; > > + > > + sps = parse_sps(data, nalu_size, ev); > > + if (!sps) { > > + av_log(avctx, AV_LOG_ERROR, "SPS parsing error\n"); > > + return AVERROR_INVALIDDATA; > > + } > > + > > + s->coded_width = sps->pic_width_in_luma_samples; > > + s->coded_height = sps->pic_height_in_luma_samples; > > + s->width = sps->pic_width_in_luma_samples - sps- > >picture_crop_left_offset - sps->picture_crop_right_offset; > > + s->height = sps->pic_height_in_luma_samples - sps- > >picture_crop_top_offset - sps->picture_crop_bottom_offset; > > + > > + SubGopLength = (int)pow(2.0, sps->log2_sub_gop_length); > > + avctx->gop_size = SubGopLength; > > + > > + avctx->delay = (sps->sps_max_dec_pic_buffering_minus1) ? > > + sps->sps_max_dec_pic_buffering_minus1 - 1 : SubGopLength + > > + sps->max_num_tid0_ref_pics - 1; > > + > > + if (sps->profile_idc == 1) avctx->profile = FF_PROFILE_EVC_MAIN; > > + else avctx->profile = FF_PROFILE_EVC_BASELINE; > > + > > + ev->time_base = avctx->time_base.den; > > This looks like a write only field. Also, avctx->time_base is no longer used for > decoding. > What you can do is setting avctx->frame_rate using > vui->num_units_in_tick and vui->time_scale if present. See h264_parser.c > > > + > > + switch (sps->chroma_format_idc) { > > + case 0: /* YCBCR400_10LE */ > > + av_log(avctx, AV_LOG_ERROR, "YCBCR400_10LE: Not supported > chroma format\n"); > > + s->format = AV_PIX_FMT_GRAY10LE; > > + return -1; > > + case 1: /* YCBCR420_10LE */ > > + s->format = AV_PIX_FMT_YUV420P10LE; > > + break; > > + case 2: /* YCBCR422_10LE */ > > + av_log(avctx, AV_LOG_ERROR, "YCBCR422_10LE: Not supported > chroma format\n"); > > + s->format = AV_PIX_FMT_YUV422P10LE; > > + return -1; > > + case 3: /* YCBCR444_10LE */ > > + av_log(avctx, AV_LOG_ERROR, "YCBCR444_10LE: Not supported > chroma format\n"); > > + s->format = AV_PIX_FMT_YUV444P10LE; > > + return -1; > > + default: > > + s->format = AV_PIX_FMT_NONE; > > + av_log(avctx, AV_LOG_ERROR, "Unknown supported chroma > format\n"); > > + return -1; > > + } > > + } else if (nalu_type == EVC_PPS_NUT) { > > + EVCParserPPS *pps; > > + > > + pps = parse_pps(data, nalu_size, ev); > > + if (!pps) { > > + av_log(avctx, AV_LOG_ERROR, "PPS parsing error\n"); > > + return AVERROR_INVALIDDATA; > > + } > > + } else if (nalu_type == EVC_SEI_NUT) // Supplemental Enhancement > Information > > + return 0; > > + else if (nalu_type == EVC_APS_NUT) // Adaptation parameter set > > + return 0; > > + else if (nalu_type == EVC_FD_NUT) /* Filler data */ > > + return 0; > > Use a switch() statement for this instead. > > > + else if (nalu_type == EVC_IDR_NUT || nalu_type == EVC_NOIDR_NUT) { // > Coded slice of a IDR or non-IDR picture > > + EVCParserSliceHeader *sh; > > + EVCParserSPS *sps; > > + int slice_pic_parameter_set_id; > > + > > + sh = parse_slice_header(data, nalu_size, ev); > > + if (!sh) { > > + av_log(avctx, AV_LOG_ERROR, "Slice header parsing error\n"); > > + return AVERROR_INVALIDDATA; > > + } > > + > > + switch (sh->slice_type) { > > + case EVC_SLICE_TYPE_B: { > > + s->pict_type = AV_PICTURE_TYPE_B; > > + break; > > + } > > + case EVC_SLICE_TYPE_P: { > > + s->pict_type = AV_PICTURE_TYPE_P; > > + break; > > + } > > + case EVC_SLICE_TYPE_I: { > > + s->pict_type = AV_PICTURE_TYPE_I; > > + break; > > + } > > + default: { > > + s->pict_type = AV_PICTURE_TYPE_NONE; > > + } > > + } > > + > > + s->key_frame = (nalu_type == EVC_IDR_NUT) ? 1 : 0; > > + > > + // POC (picture order count of the current picture) derivation > > + // @see ISO/IEC 23094-1:2020(E) 8.3.1 Decoding process for picture > order count > > + slice_pic_parameter_set_id = sh->slice_pic_parameter_set_id; > > + sps = &ev->sps[slice_pic_parameter_set_id]; > > + > > + if (sps->sps_pocs_flag) { > > + > > + int PicOrderCntMsb = 0; > > + ev->poc.prevPicOrderCntVal = ev->poc.PicOrderCntVal; > > + > > + if (nalu_type == EVC_IDR_NUT) > > + PicOrderCntMsb = 0; > > + else { > > + int MaxPicOrderCntLsb = 1 << (sps- > >log2_max_pic_order_cnt_lsb_minus4 + 4); > > + > > + int prevPicOrderCntLsb = ev->poc.PicOrderCntVal & > (MaxPicOrderCntLsb - 1); > > + int prevPicOrderCntMsb = ev->poc.PicOrderCntVal - > prevPicOrderCntLsb; > > + > > + > > + if ((sh->slice_pic_order_cnt_lsb < prevPicOrderCntLsb) && > > + ((prevPicOrderCntLsb - sh->slice_pic_order_cnt_lsb) >= > (MaxPicOrderCntLsb / 2))) > > + > > + PicOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb; > > + > > + else if ((sh->slice_pic_order_cnt_lsb > prevPicOrderCntLsb) && > > + ((sh->slice_pic_order_cnt_lsb - prevPicOrderCntLsb) > > (MaxPicOrderCntLsb / 2))) > > + > > + PicOrderCntMsb = prevPicOrderCntMsb - MaxPicOrderCntLsb; > > + > > + else > > + PicOrderCntMsb = prevPicOrderCntMsb; > > + } > > + ev->poc.PicOrderCntVal = PicOrderCntMsb + sh- > >slice_pic_order_cnt_lsb; > > + > > + } else { > > + if (nalu_type == EVC_IDR_NUT) { > > + ev->poc.PicOrderCntVal = 0; > > + ev->poc.DocOffset = -1; > > + } else { > > + int SubGopLength = (int)pow(2.0, sps->log2_sub_gop_length); > > + if (tid == 0) { > > + ev->poc.PicOrderCntVal = ev->poc.prevPicOrderCntVal + > SubGopLength; > > + ev->poc.DocOffset = 0; > > + ev->poc.prevPicOrderCntVal = ev->poc.PicOrderCntVal; > > + } else { > > + int ExpectedTemporalId; > > + int PocOffset; > > + int prevDocOffset = ev->poc.DocOffset; > > + > > + ev->poc.DocOffset = (prevDocOffset + 1) % SubGopLength; > > + if (ev->poc.DocOffset == 0) { > > + ev->poc.prevPicOrderCntVal += SubGopLength; > > + ExpectedTemporalId = 0; > > + } else > > + ExpectedTemporalId = 1 + (int)log2(ev->poc.DocOffset); > > + while (tid != ExpectedTemporalId) { > > + ev->poc.DocOffset = (ev->poc.DocOffset + 1) % SubGopLength; > > + if (ev->poc.DocOffset == 0) > > + ExpectedTemporalId = 0; > > + else > > + ExpectedTemporalId = 1 + (int)log2(ev->poc.DocOffset); > > + } > > + PocOffset = (int)(SubGopLength * ((2.0 * ev->poc.DocOffset + 1) / > (int)pow(2.0, tid) - 2)); > > + ev->poc.PicOrderCntVal = ev->poc.prevPicOrderCntVal + > PocOffset; > > + } > > + } > > + } > > + > > + s->output_picture_number = ev->poc.PicOrderCntVal; > > + s->key_frame = (nalu_type == EVC_IDR_NUT) ? 1 : 0; > > + > > + return 0; > > + } > > + data += (nalu_size - EVC_NALU_HEADER_SIZE); > > + data_size -= (nalu_size - EVC_NALU_HEADER_SIZE); > > What does this even do? They are local variables, and this is not in a loop. > > > + > > + return 0; > > +} > > [...] > > > +// Find the end of the current frame in the bitstream. > > +// The end of frame is the end of Access Unit. > > +// Function returns the position of the first byte of the next frame, or > END_NOT_FOUND > > +static int evc_find_frame_end(AVCodecParserContext *s, const uint8_t *buf, > > + int buf_size, AVCodecContext *avctx) > > +{ > > + EVCParserContext *ctx = s->priv_data; > > + > > + const uint8_t *data = buf; > > + int data_size = buf_size; > > + > > + while (data_size > 0) { > > + > > + if (ctx->to_read == 0) { > > + // Nothing must be read and appended to the data from previous > chunks. > > + // The previous chunk of data provided the complete NALU prefix or > provided the complete NALU. > > + > > + if (ctx->nalu_prefix_assembled) // NALU prefix has been assembled > from previous and current chunks of incoming data > > + ctx->nalu_prefix_assembled = 0; > > + else { // Buffer size is not enough for buffer to store NAL unit 4-bytes > prefix (length) > > + if (data_size < EVC_NALU_LENGTH_PREFIX_SIZE) { > > + ctx->to_read = EVC_NALU_LENGTH_PREFIX_SIZE - data_size; > > + ctx->incomplete_nalu_prefix_read = 1; > > + return END_NOT_FOUND; > > + } > > + > > + ctx->nalu_size = read_nal_unit_length(data, data_size, avctx); > > + ctx->bytes_read += EVC_NALU_LENGTH_PREFIX_SIZE; > > + > > + data += EVC_NALU_LENGTH_PREFIX_SIZE; > > + data_size -= EVC_NALU_LENGTH_PREFIX_SIZE; > > + } > > + > > + if (data_size < ctx->nalu_size) { > > + > > + ctx->to_read = ctx->nalu_size - data_size; > > + ctx->incomplete_nalu_read = 1; > > + return END_NOT_FOUND; > > + } > > + > > + // the entire NALU can be read > > + if (parse_nal_unit(s, data, ctx->nalu_size, avctx) != 0) { > > NALU parsing should happen always, not only when the parser is required > to assemble a full access unit. > > > + av_log(avctx, AV_LOG_ERROR, "Parsing of NAL unit failed\n"); > > + return AVERROR_INVALIDDATA; > > + } > > + > > + data += ctx->nalu_size; > > + data_size -= ctx->nalu_size; > > + > > + ctx->bytes_read += ctx->nalu_size; > > + > > + if (end_of_access_unit_found(s, avctx)) { > > + > > + // parser should return buffer that contains complete AU > > + int read_bytes = ctx->bytes_read; > > + ctx->bytes_read = 0; > > + return read_bytes; > > + } > > + > > + // go to the next iteration > > + continue; > > + > > + } else { > > + // The previous chunk of input data did not contain the complete valid > NALU prefix or did not contain the complete NALU. > > + // > > + // Missing data must be read from the current data chunk and merged > with the data from the previous data chunk > > + // to assemble a complete NALU or complete NALU prefix. > > + // > > + // The data from the previous data chunk are stored in pc->buf > > + > > + if (ctx->to_read < data_size) { > > + > > + if (ctx->incomplete_nalu_prefix_read == 1) { > > + > > + uint8_t nalu_prefix[EVC_NALU_LENGTH_PREFIX_SIZE]; > > + evc_assemble_nalu_prefix(s, data, data_size, nalu_prefix, avctx); > > + > > + ctx->nalu_size = read_nal_unit_length(nalu_prefix, > EVC_NALU_LENGTH_PREFIX_SIZE, avctx); > > + > > + // update variable storing amout of read bytes for teh current AU > > + ctx->bytes_read += ctx->to_read; > > + > > + // update data pointer and data size > > + data += ctx->to_read; > > + data_size -= ctx->to_read; > > + > > + // reset variable storing amount of bytes to read from the new > data chunk > > + ctx->to_read = 0; > > + > > + ctx->incomplete_nalu_prefix_read = 0; > > + ctx->nalu_prefix_assembled = 1; > > + > > + continue; > > + } > > + if (ctx->incomplete_nalu_read == 1) { > > + > > + uint8_t *nalu = (uint8_t *)av_malloc(ctx->nalu_size); > > + > > + // assemble NAL unit using data from previous data chunks (pc- > >buffer) and the current one (data) > > + evc_assemble_nalu(s, data, ctx->to_read, nalu, ctx->nalu_size, > avctx); > > + > > + if (parse_nal_unit(s, nalu, ctx->nalu_size, avctx) != 0) { > > + av_log(avctx, AV_LOG_ERROR, "Parsing of NAL unit failed\n"); > > + return AVERROR_INVALIDDATA; > > + } > > + av_free(nalu); > > + > > + // update variable storing amout of read bytes for teh current AU > > + ctx->bytes_read += ctx->nalu_size; > > + > > + // update data pointer and data size > > + data += ctx->to_read; > > + data_size -= ctx->to_read; > > + > > + ctx->incomplete_nalu_read = 0; > > + > > + if (end_of_access_unit_found(s, avctx)) { > > + > > + // parser should return buffer that contains complete AU > > + int read_bytes = ctx->to_read; > > + > > + ctx->to_read = 0; > > + ctx->bytes_read = 0; > > + > > + return read_bytes; > > + } > > + > > + // reset variable storing amount of bytes to read from the new > data chunk > > + ctx->to_read = 0; > > + > > + continue; > > + } > > + } else { > > + // needed more input data to assemble complete valid NAL Unit > > + ctx->to_read = ctx->to_read - data_size; > > + return END_NOT_FOUND; > > + } > > + } > > + } > > + > > + return END_NOT_FOUND; > > +} > > + > > +static int evc_parse(AVCodecParserContext *s, AVCodecContext *avctx, > > + const uint8_t **poutbuf, int *poutbuf_size, > > + const uint8_t *buf, int buf_size) > > +{ > > + int next; > > + EVCParserContext *ev = s->priv_data; > > + ParseContext *pc = &ev->pc; > > + > > + if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) > > + next = buf_size; > > + else { > > + next = evc_find_frame_end(s, buf, buf_size, avctx); > > + if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) { > > + *poutbuf = NULL; > > + *poutbuf_size = 0; > > + return buf_size; > > + } > > + } > > Case in point, you should call the function to parse the packet's NALUs > here, so it's done also when PARSER_FLAG_COMPLETE_FRAMES is signaled. > > > + > > + // poutbuf contains just one Access Unit > > + *poutbuf = buf; > > + *poutbuf_size = buf_size; > > + > > + return next; > > +} > > + > > +static int evc_parser_init(AVCodecParserContext *s) > > +{ > > + EVCParserContext *ev = s->priv_data; > > + ev->incomplete_nalu_prefix_read = 0; > > + > > + return 0; > > +} > > + > > +const AVCodecParser ff_evc_parser = { > > + .codec_ids = { AV_CODEC_ID_EVC }, > > + .priv_data_size = sizeof(EVCParserContext), > > + .parser_init = evc_parser_init, > > + .parser_parse = evc_parse, > > + .parser_close = ff_parse_close, > > +}; > > diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c > > index d355808018..2c077ec3ae 100644 > > --- a/libavcodec/parsers.c > > +++ b/libavcodec/parsers.c > > @@ -41,6 +41,7 @@ extern const AVCodecParser ff_dvaudio_parser; > > extern const AVCodecParser ff_dvbsub_parser; > > extern const AVCodecParser ff_dvdsub_parser; > > extern const AVCodecParser ff_dvd_nav_parser; > > +extern const AVCodecParser ff_evc_parser; > > extern const AVCodecParser ff_flac_parser; > > extern const AVCodecParser ff_ftr_parser; > > extern const AVCodecParser ff_g723_1_parser; > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://protect2.fireeye.com/v1/url?k=e23962f4-bda25bf8-e238e9bb- > 000babff3563-acac272a35bdd12c&q=1&e=daa29860-f1f6-44c2-9ab6- > d68c3d42950a&u=https%3A%2F%2Fffmpeg.org%2Fmailman%2Flistinfo%2Fffmp > eg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". We've just uploaded new patches. Everything you have requested has been fixed. _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
prev parent reply other threads:[~2023-04-05 8:59 UTC|newest] Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top [not found] <CGME20230328134650eucas1p16239b9b4a13a09b2baecdd2012fd01c5@eucas1p1.samsung.com> 2023-03-28 13:46 ` Dawid Kozinski 2023-03-29 14:57 ` James Almer 2023-04-05 8:59 ` Dawid Kozinski/Multimedia (PLT) /SRPOL/Staff Engineer/Samsung Electronics [this message]
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to='000001d9679c$fa09ddd0$ee1d9970$@samsung.com' \ --to=d.kozinski@samsung.com \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git