From: Zhao Zhili <quinkblack-at-foxmail.com@ffmpeg.org> To: ffmpeg-devel@ffmpeg.org Subject: Re: [FFmpeg-devel] [PATCH v2 3/3] avcodec/ohenc: Add h264/hevc OpenHarmony encoders Date: Sun, 6 Jul 2025 17:01:38 +0800 Message-ID: <tencent_039F44AB154E0B70432D579A0E40519AE008@qq.com> (raw) In-Reply-To: <tencent_45ADD23845C311B57C6996028D69767D9E0A@qq.com> > 在 2025年7月6日,下午4:06,Zhao Zhili <quinkblack@foxmail.com> 写道: > > From: Zhao Zhili <zhilizhao@tencent.com> > > --- > Changelog | 1 + > configure | 7 +- > libavcodec/Makefile | 2 + > libavcodec/allcodecs.c | 2 + > libavcodec/ohcodec.c | 8 + > libavcodec/ohcodec.h | 1 + > libavcodec/ohenc.c | 693 +++++++++++++++++++++++++++++++++++++++++ > libavcodec/version.h | 2 +- > 8 files changed, 713 insertions(+), 3 deletions(-) > create mode 100644 libavcodec/ohenc.c > > diff --git a/Changelog b/Changelog > index f5ec9eafc3..4a8e0ef60d 100644 > --- a/Changelog > +++ b/Changelog > @@ -21,6 +21,7 @@ version <next>: > - G.728 decoder > - pad_cuda filter > - Sanyo LD-ADPCM decoder > +- OpenHarmony hardware decoder/encoder > > > version 7.1: > diff --git a/configure b/configure > index a2ace36cdc..225aaaee99 100755 > --- a/configure > +++ b/configure > @@ -3412,6 +3412,7 @@ h264_nvenc_encoder_deps="nvenc" > h264_nvenc_encoder_select="atsc_a53" > h264_oh_decoder_deps="ohcodec" > h264_oh_decoder_select="h264_mp4toannexb_bsf" > +h264_oh_encoder_deps="ohcodec" > h264_omx_encoder_deps="omx" > h264_qsv_decoder_select="h264_mp4toannexb_bsf qsvdec" > h264_qsv_encoder_select="atsc_a53 qsvenc" > @@ -3436,6 +3437,7 @@ hevc_nvenc_encoder_deps="nvenc" > hevc_nvenc_encoder_select="atsc_a53" > hevc_oh_decoder_deps="ohcodec" > hevc_oh_decoder_select="hevc_mp4toannexb_bsf" > +hevc_oh_encoder_deps="ohcodec" > hevc_qsv_decoder_select="hevc_mp4toannexb_bsf qsvdec" > hevc_qsv_encoder_select="hevcparse qsvenc" > hevc_rkmpp_decoder_deps="rkmpp" > @@ -7171,8 +7173,9 @@ enabled mmal && { check_lib mmal interface/mmal/mmal.h mmal_port_co > check_lib mmal interface/mmal/mmal.h mmal_port_connect -lmmal_core -lmmal_util -lmmal_vc_client -lbcm_host; } || > die "ERROR: mmal not found" && > check_func_headers interface/mmal/mmal.h "MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS"; } > -enabled ohcodec && { check_lib ohcodec "multimedia/player_framework/native_avcodec_videodecoder.h" \ > - OH_VideoDecoder_CreateByName -lnative_media_codecbase -lnative_media_core -lnative_media_vdec || > +enabled ohcodec && { check_lib ohcodec "multimedia/player_framework/native_avcodec_videodecoder.h multimedia/player_framework/native_avcodec_videoencoder.h" \ > + "OH_VideoDecoder_CreateByName OH_VideoEncoder_CreateByName" \ > + -lnative_media_vdec -lnative_media_venc -lnative_media_codecbase -lnative_media_core || > die "ERROR: missing native_media libs"; } > enabled openal && { check_pkg_config openal "openal >= 1.1" "AL/al.h" alGetError || > { for al_extralibs in "${OPENAL_LIBS}" "-lopenal" "-lOpenAL32"; do > diff --git a/libavcodec/Makefile b/libavcodec/Makefile > index b784bf3f7e..f935cfbe0d 100644 > --- a/libavcodec/Makefile > +++ b/libavcodec/Makefile > @@ -429,6 +429,7 @@ OBJS-$(CONFIG_H264_MF_ENCODER) += mfenc.o mf_utils.o > OBJS-$(CONFIG_H264_MMAL_DECODER) += mmaldec.o > OBJS-$(CONFIG_H264_NVENC_ENCODER) += nvenc_h264.o nvenc.o > OBJS-$(CONFIG_H264_OH_DECODER) += ohcodec.o ohdec.o > +OBJS-$(CONFIG_H264_OH_ENCODER) += ohcodec.o ohenc.o > OBJS-$(CONFIG_H264_OMX_ENCODER) += omx.o > OBJS-$(CONFIG_H264_QSV_DECODER) += qsvdec.o > OBJS-$(CONFIG_H264_QSV_ENCODER) += qsvenc_h264.o > @@ -458,6 +459,7 @@ OBJS-$(CONFIG_HEVC_MEDIACODEC_ENCODER) += mediacodecenc.o > OBJS-$(CONFIG_HEVC_MF_ENCODER) += mfenc.o mf_utils.o > OBJS-$(CONFIG_HEVC_NVENC_ENCODER) += nvenc_hevc.o nvenc.o > OBJS-$(CONFIG_HEVC_OH_DECODER) += ohcodec.o ohdec.o > +OBJS-$(CONFIG_HEVC_OH_ENCODER) += ohcodec.o ohenc.o > OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec.o > OBJS-$(CONFIG_HEVC_QSV_ENCODER) += qsvenc_hevc.o hevc/ps_enc.o > OBJS-$(CONFIG_HEVC_RKMPP_DECODER) += rkmppdec.o > diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c > index 826ccf7329..3aaa351157 100644 > --- a/libavcodec/allcodecs.c > +++ b/libavcodec/allcodecs.c > @@ -861,6 +861,7 @@ extern const FFCodec ff_h264_cuvid_decoder; > extern const FFCodec ff_h264_mf_encoder; > extern const FFCodec ff_h264_nvenc_encoder; > extern const FFCodec ff_h264_oh_decoder; > +extern const FFCodec ff_h264_oh_encoder; > extern const FFCodec ff_h264_omx_encoder; > extern const FFCodec ff_h264_qsv_encoder; > extern const FFCodec ff_h264_v4l2m2m_encoder; > @@ -876,6 +877,7 @@ extern const FFCodec ff_hevc_mediacodec_encoder; > extern const FFCodec ff_hevc_mf_encoder; > extern const FFCodec ff_hevc_nvenc_encoder; > extern const FFCodec ff_hevc_oh_decoder; > +extern const FFCodec ff_hevc_oh_encoder; > extern const FFCodec ff_hevc_qsv_encoder; > extern const FFCodec ff_hevc_v4l2m2m_encoder; > extern const FFCodec ff_hevc_vaapi_encoder; > diff --git a/libavcodec/ohcodec.c b/libavcodec/ohcodec.c > index 284ff1b2ec..f8a6faaba5 100644 > --- a/libavcodec/ohcodec.c > +++ b/libavcodec/ohcodec.c > @@ -69,3 +69,11 @@ enum AVPixelFormat ff_oh_pix_to_ff_pix(OH_AVPixelFormat oh_pix) > return AV_PIX_FMT_NONE; > } > > +int ff_oh_pix_from_ff_pix(enum AVPixelFormat pix) > +{ > + for (size_t i = 0; i < FF_ARRAY_ELEMS(oh_pix_map); i++) > + if (oh_pix_map[i].pix == pix) > + return oh_pix_map[i].oh_pix; > + > + return 0; > +} > diff --git a/libavcodec/ohcodec.h b/libavcodec/ohcodec.h > index 236ddba67e..074747247b 100644 > --- a/libavcodec/ohcodec.h > +++ b/libavcodec/ohcodec.h > @@ -52,5 +52,6 @@ static inline const char *ff_oh_mime(enum AVCodecID codec_id, void *log) > } > > enum AVPixelFormat ff_oh_pix_to_ff_pix(OH_AVPixelFormat oh_pix); > +int ff_oh_pix_from_ff_pix(enum AVPixelFormat pix); > > #endif > diff --git a/libavcodec/ohenc.c b/libavcodec/ohenc.c > new file mode 100644 > index 0000000000..70b17dc029 > --- /dev/null > +++ b/libavcodec/ohenc.c > @@ -0,0 +1,693 @@ > +/* > + * This file is part of FFmpeg. > + * > + * Copyright (c) 2025 Zhao Zhili <quinkblack@foxmail.com> > + * > + * FFmpeg is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * FFmpeg is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with FFmpeg; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA > + */ > + > +#include "config_components.h" > + > +#include <stdbool.h> > +#include <multimedia/player_framework/native_avcapability.h> > +#include <multimedia/player_framework/native_avcodec_videoencoder.h> > + > +#include "libavutil/fifo.h" > +#include "libavutil/imgutils.h" > +#include "libavutil/mem.h" > +#include "libavutil/opt.h" > +#include "libavutil/thread.h" > + > +#include "avcodec.h" > +#include "codec_internal.h" > +#include "encode.h" > +#include "hwconfig.h" > +#include "ohcodec.h" > + > +typedef struct OHCodecEncContext { > + AVClass *avclass; > + OH_AVCodec *enc; > + > + AVMutex input_mutex; > + AVCond input_cond; > + AVFifo *input_queue; > + > + AVMutex output_mutex; > + AVCond output_cond; > + AVFifo *output_queue; > + > + AVFrame *frame; > + uint8_t *extradata; > + int extradata_size; > + > + int encode_status; > + bool eof_sent; > + > + bool got_stream_info; > + int stride; > + int slice_height; > + > + uint64_t native_window; > + char *name; > + int allow_sw; > + int bitrate_mode; > +} OHCodecEncContext; > + > +static const enum AVPixelFormat ohcodec_pix_fmts[] = { > + AV_PIX_FMT_OHCODEC, > + AV_PIX_FMT_NV12, > + AV_PIX_FMT_NONE > +}; > + > +static int oh_encode_create(OHCodecEncContext *s, AVCodecContext *avctx) > +{ > + const char *name = s->name; > + > + if (!name) { > + const char *mime = ff_oh_mime(avctx->codec_id, avctx); > + if (!mime) > + return AVERROR_BUG; > + OH_AVCapability *cap = OH_AVCodec_GetCapabilityByCategory(mime, true, HARDWARE); > + if (!cap) { > + if (!s->allow_sw) { > + av_log(avctx, AV_LOG_ERROR, "Failed to get hardware codec %s\n", mime); > + return AVERROR_EXTERNAL; > + } > + av_log(avctx, AV_LOG_WARNING, > + "Failed to get hardware codec %s, try software backend\n", mime); > + cap = OH_AVCodec_GetCapabilityByCategory(mime, true, SOFTWARE); > + if (!cap) { > + av_log(avctx, AV_LOG_ERROR, "Failed to get software codec %s\n", mime); > + return AVERROR_EXTERNAL; > + } > + } > + name = OH_AVCapability_GetName(cap); > + if (!name) > + return AVERROR_EXTERNAL; > + } > + > + s->enc = OH_VideoEncoder_CreateByName(name); > + if (!s->enc) { > + av_log(avctx, AV_LOG_ERROR, "Create encoder with name %s failed\n", name); > + return AVERROR_EXTERNAL; > + } > + av_log(avctx, AV_LOG_DEBUG, "Create encoder %s success\n", name); > + > + return 0; > +} > + > +static int oh_encode_set_format(OHCodecEncContext *s, AVCodecContext *avctx) > +{ > + int ret; > + > + OH_AVFormat *format = OH_AVFormat_Create(); > + if (!format) > + return AVERROR(ENOMEM); > + > + bool b = OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, avctx->width); > + b = b && OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, avctx->height); > + if (!b) { > + av_log(avctx, AV_LOG_ERROR, "Set width/height (%dx%d) failed\n", > + avctx->width, avctx->height); > + ret = AVERROR_EXTERNAL; > + goto out; > + } > + if (avctx->framerate.num && avctx->framerate.den) > + OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, > + av_q2d(avctx->framerate)); > + int pix = ff_oh_pix_from_ff_pix(avctx->pix_fmt); > + if (!pix) { > + ret = AVERROR_BUG; > + goto out; > + } > + b = OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, pix); > + if (!b) { > + av_log(avctx, AV_LOG_ERROR, "Set pixel format to %d failed\n", pix); > + ret = AVERROR_EXTERNAL; > + goto out; > + } > + > + if (s->bitrate_mode != -1) { > + b = OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODE_BITRATE_MODE, s->bitrate_mode); > + if (!b) { > + av_log(avctx, AV_LOG_ERROR, "Set bitrate mode to %d failed\n", > + s->bitrate_mode); > + ret = AVERROR_EXTERNAL; > + goto out; > + } > + } > + OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, avctx->bit_rate); > + > + if (avctx->gop_size > 0) { > + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { > + // In milliseconds > + int gop = av_rescale_q(avctx->gop_size, > + av_make_q(avctx->framerate.den, > + avctx->framerate.num), > + av_make_q(1, 1000)); > + OH_AVFormat_SetIntValue(format, OH_MD_KEY_I_FRAME_INTERVAL, gop); > + } else { > + av_log(avctx, AV_LOG_WARNING, "Skip setting gop without framerate\n"); > + } > + } else if (!avctx->gop_size) { > + // All frames are key frame > + OH_AVFormat_SetIntValue(format, OH_MD_KEY_I_FRAME_INTERVAL, 0); > + } else if (avctx->gop_size == -1) { > + // Infinite gop > + OH_AVFormat_SetIntValue(format, OH_MD_KEY_I_FRAME_INTERVAL, -1); > + } > + > + OH_AVErrCode err = OH_VideoEncoder_Configure(s->enc, format); > + if (err != AV_ERR_OK) { > + ret = ff_oh_err_to_ff_err(err); > + av_log(avctx, AV_LOG_ERROR, "Decoder configure failed, %d, %s\n", > + err, av_err2str(ret)); > + goto out; > + } > + > + if (avctx->pix_fmt == AV_PIX_FMT_OHCODEC) { > + OHNativeWindow *window; > + err = OH_VideoEncoder_GetSurface(s->enc, &window); > + if (err != AV_ERR_OK) { > + ret = ff_oh_err_to_ff_err(err); > + av_log(avctx, AV_LOG_ERROR, "Get surface failed, %d, %s\n", > + err, av_err2str(ret)); > + goto out; > + } > + s->native_window = (uintptr_t)window; > + av_log(avctx, AV_LOG_INFO, "native window %p\n", window); I’ve given it some more thought, native windows could be exported via hwcontext device. It’s less hack than this. Will be fixed in v3. > + } > + > + return 0; > +out: > + OH_AVFormat_Destroy(format); > + return ret; > +} > + > +static void oh_encode_on_err(OH_AVCodec *codec, int32_t err, void *userdata) > +{ > + AVCodecContext *avctx = userdata; > + OHCodecEncContext *s = avctx->priv_data; > + > + // Careful on the lock order. > + // Always lock input first. > + ff_mutex_lock(&s->input_mutex); > + ff_mutex_lock(&s->output_mutex); > + s->encode_status = ff_oh_err_to_ff_err(err); > + ff_mutex_unlock(&s->output_mutex); > + ff_mutex_unlock(&s->input_mutex); > + > + ff_cond_signal(&s->output_cond); > + ff_cond_signal(&s->input_cond); > +} > + > +static void oh_encode_on_stream_changed(OH_AVCodec *codec, OH_AVFormat *format, > + void *userdata) > +{ > + AVCodecContext *avctx = userdata; > + OHCodecEncContext *s = avctx->priv_data; > + > + if (!OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_STRIDE, &s->stride)) > + s->stride = avctx->width; > + if (!OH_AVFormat_GetIntValue(format, OH_MD_KEY_VIDEO_SLICE_HEIGHT, &s->slice_height)) > + s->slice_height = avctx->height; > + > + s->got_stream_info = true; > +} > + > +static void oh_encode_on_need_input(OH_AVCodec *codec, uint32_t index, > + OH_AVBuffer *buffer, void *userdata) > +{ > + AVCodecContext *avctx = userdata; > + OHCodecEncContext *s = avctx->priv_data; > + OHBufferQueueItem item = { > + index, buffer, > + }; > + > + ff_mutex_lock(&s->input_mutex); > + int ret = av_fifo_write(s->input_queue, &item, 1); > + if (ret >= 0) > + ff_cond_signal(&s->input_cond); > + ff_mutex_unlock(&s->input_mutex); > + > + if (ret < 0) > + oh_encode_on_err(codec, AV_ERR_NO_MEMORY, userdata); > +} > + > +static void oh_encode_on_output(OH_AVCodec *codec, uint32_t index, > + OH_AVBuffer *buffer, void *userdata) > +{ > + AVCodecContext *avctx = userdata; > + OHCodecEncContext *s = avctx->priv_data; > + OHBufferQueueItem item = { > + index, buffer, > + }; > + > + ff_mutex_lock(&s->output_mutex); > + int ret = av_fifo_write(s->output_queue, &item, 1); > + if (ret >= 0) > + ff_cond_signal(&s->output_cond); > + ff_mutex_unlock(&s->output_mutex); > + > + if (ret < 0) > + oh_encode_on_err(codec, AV_ERR_NO_MEMORY, userdata); > +} > + > +static int oh_encode_start(OHCodecEncContext *s, AVCodecContext *avctx) > +{ > + int ret; > + OH_AVErrCode err; > + OH_AVCodecCallback cb = { > + .onError = oh_encode_on_err, > + .onStreamChanged = oh_encode_on_stream_changed, > + .onNeedInputBuffer = oh_encode_on_need_input, > + .onNewOutputBuffer = oh_encode_on_output, > + }; > + > + err = OH_VideoEncoder_RegisterCallback(s->enc, cb, avctx); > + if (err != AV_ERR_OK) { > + ret = ff_oh_err_to_ff_err(err); > + av_log(avctx, AV_LOG_ERROR, "Register callback failed, %d, %s\n", > + err, av_err2str(ret)); > + return ret; > + } > + err = OH_VideoEncoder_Prepare(s->enc); > + if (err != AV_ERR_OK) { > + ret = ff_oh_err_to_ff_err(err); > + av_log(avctx, AV_LOG_ERROR, "Prepare failed, %d, %s\n", > + err, av_err2str(ret)); > + return ret; > + } > + err = OH_VideoEncoder_Start(s->enc); > + if (err != AV_ERR_OK) { > + ret = ff_oh_err_to_ff_err(err); > + av_log(avctx, AV_LOG_ERROR, "Start failed, %d, %s\n", > + err, av_err2str(ret)); > + return ret; > + } > + > + return 0; > +} > + > +static av_cold int oh_encode_init(AVCodecContext *avctx) > +{ > + OHCodecEncContext *s = avctx->priv_data; > + > + // Initialize these fields first, so oh_decode_close can destroy them safely > + ff_mutex_init(&s->input_mutex, NULL); > + ff_cond_init(&s->input_cond, NULL); > + ff_mutex_init(&s->output_mutex, NULL); > + ff_cond_init(&s->output_cond, NULL); > + > + int ret = oh_encode_create(s, avctx); > + if (ret < 0) > + return ret; > + ret = oh_encode_set_format(s, avctx); > + if (ret < 0) > + return ret; > + > + size_t fifo_size = 16; > + s->input_queue = av_fifo_alloc2(fifo_size, sizeof(OHBufferQueueItem), > + AV_FIFO_FLAG_AUTO_GROW); > + s->output_queue = av_fifo_alloc2(fifo_size, sizeof(OHBufferQueueItem), > + AV_FIFO_FLAG_AUTO_GROW); > + s->frame = av_frame_alloc(); > + if (!s->input_queue || !s->output_queue || !s->frame) > + return AVERROR(ENOMEM); > + > + ret = oh_encode_start(s, avctx); > + if (ret < 0) > + return ret; > + > + return 0; > +} > + > +static av_cold int oh_encode_close(AVCodecContext *avctx) > +{ > + OHCodecEncContext *s = avctx->priv_data; > + > + if (s->enc) { > + OH_VideoEncoder_Stop(s->enc); > + OH_AVErrCode err = OH_VideoEncoder_Destroy(s->enc); > + if (err == AV_ERR_OK) > + av_log(avctx, AV_LOG_DEBUG, "Destroy encoder success\n"); > + else > + av_log(avctx, AV_LOG_ERROR, "Destroy decoder failed, %d, %s\n", > + err, av_err2str(ff_oh_err_to_ff_err(err))); > + } > + > + av_freep(&s->extradata); > + av_frame_free(&s->frame); > + > + ff_mutex_destroy(&s->input_mutex); > + ff_cond_destroy(&s->input_cond); > + av_fifo_freep2(&s->input_queue); > + > + ff_mutex_destroy(&s->output_mutex); > + ff_cond_destroy(&s->output_cond); > + av_fifo_freep2(&s->output_queue); > + > + return 0; > +} > + > +static int oh_encode_output_packet(AVCodecContext *avctx, AVPacket *pkt, > + OHBufferQueueItem *output) > +{ > + OHCodecEncContext *s = avctx->priv_data; > + uint8_t *p; > + OH_AVCodecBufferAttr attr; > + int ret; > + > + OH_AVErrCode err = OH_AVBuffer_GetBufferAttr(output->buffer, &attr); > + if (err != AV_ERR_OK) { > + ret = ff_oh_err_to_ff_err(err); > + goto out; > + } > + if (attr.flags & AVCODEC_BUFFER_FLAGS_EOS) { > + av_log(avctx, AV_LOG_DEBUG, "Buffer flag eos\n"); > + ret = AVERROR_EOF; > + goto out; > + } > + > + p = OH_AVBuffer_GetAddr(output->buffer); > + if (!p) { > + av_log(avctx, AV_LOG_ERROR, "Failed to get output buffer addr\n"); > + ret = AVERROR_EXTERNAL; > + goto out; > + } > + if (attr.flags & AVCODEC_BUFFER_FLAGS_CODEC_DATA) { > + av_freep(&s->extradata); > + s->extradata = av_malloc(attr.size + AV_INPUT_BUFFER_PADDING_SIZE); > + if (!s->extradata) { > + ret = AVERROR(ENOMEM); > + goto out; > + } > + memset(s->extradata + attr.size, 0, AV_INPUT_BUFFER_PADDING_SIZE); > + memcpy(s->extradata, p + attr.offset, attr.size); > + s->extradata_size = attr.size; > + ret = 0; > + goto out; > + } > + > + int64_t extradata_size = s->extradata_size; > + s->extradata_size = 0; > + > + if (extradata_size && (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) { > + ret = av_packet_add_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, > + s->extradata, extradata_size); > + if (ret < 0) > + goto out; > + s->extradata = NULL; > + extradata_size = 0; > + } > + > + ret = ff_get_encode_buffer(avctx, pkt, attr.size + extradata_size, 0); > + if (ret < 0) > + goto out; > + > + if (extradata_size) > + memcpy(pkt->data, s->extradata, extradata_size); > + > + memcpy(pkt->data + extradata_size, p + attr.offset, attr.size); > + pkt->pts = av_rescale_q(attr.pts, AV_TIME_BASE_Q, avctx->time_base); > + if (attr.flags & AVCODEC_BUFFER_FLAGS_SYNC_FRAME) > + pkt->flags |= AV_PKT_FLAG_KEY; > + ret = 0; > +out: > + OH_VideoEncoder_FreeOutputBuffer(s->enc, output->index); > + return ret; > +} > + > +static int oh_encode_send_hw_frame(AVCodecContext *avctx) > +{ > + OHCodecEncContext *s = avctx->priv_data; > + > + if (s->eof_sent) > + return 0; > + > + if (s->frame->buf[0]) { > + av_frame_unref(s->frame); > + return 0; > + } > + > + OH_AVErrCode err = OH_VideoEncoder_NotifyEndOfStream(s->enc); > + s->eof_sent = true; > + return ff_oh_err_to_ff_err(err); > +} > + > +static int oh_encode_send_sw_frame(AVCodecContext *avctx, OHBufferQueueItem *input) > +{ > + OHCodecEncContext *s = avctx->priv_data; > + AVFrame *frame = s->frame; > + OH_AVErrCode err; > + int ret; > + > + if (!s->got_stream_info) { > + // This shouldn't happen, add a warning message. > + av_log(avctx, AV_LOG_WARNING, > + "decoder didn't notify stream info, try get format explicitly\n"); > + > + OH_AVFormat *format = OH_VideoEncoder_GetOutputDescription(s->enc); > + if (!format) { > + av_log(avctx, AV_LOG_ERROR, "GetOutputDescription failed\n"); > + return AVERROR_EXTERNAL; > + } > + > + oh_encode_on_stream_changed(s->enc, format, avctx); > + OH_AVFormat_Destroy(format); > + if (!s->got_stream_info) > + return AVERROR_EXTERNAL; > + } > + > + if (!frame->buf[0] && !s->eof_sent) { > + OH_AVCodecBufferAttr attr = { > + .flags = AVCODEC_BUFFER_FLAGS_EOS, > + }; > + err = OH_AVBuffer_SetBufferAttr(input->buffer, &attr); > + if (err != AV_ERR_OK) > + return ff_oh_err_to_ff_err(err); > + err = OH_VideoEncoder_PushInputBuffer(s->enc, input->index); > + if (err != AV_ERR_OK) > + return ff_oh_err_to_ff_err(err); > + s->eof_sent = true; > + return 0; > + } > + > + uint8_t *p = OH_AVBuffer_GetAddr(input->buffer); > + int32_t n = OH_AVBuffer_GetCapacity(input->buffer); > + if (!p || n <= 0) { > + av_log(avctx, AV_LOG_ERROR, > + "Failed to get buffer addr (%p) or capacity (%d)\n", > + p, n); > + return AVERROR_EXTERNAL; > + } > + > + uint8_t *dst[4] = {0}; > + int dst_linesizes[4] = {0}; > + ret = av_image_fill_linesizes(dst_linesizes, frame->format, s->stride); > + if (ret < 0) > + return ret; > + ret = av_image_fill_pointers(dst, frame->format, s->slice_height, p, > + dst_linesizes); > + if (ret < 0) > + return ret; > + > + av_image_copy2(dst, dst_linesizes, frame->data, frame->linesize, > + frame->format, frame->width, frame->height); > + OH_AVCodecBufferAttr attr = { > + .size = n, > + .offset = 0, > + .pts = av_rescale_q(s->frame->pts, avctx->pkt_timebase, > + AV_TIME_BASE_Q), > + .flags = (s->frame->flags & AV_FRAME_FLAG_KEY) > + ? AVCODEC_BUFFER_FLAGS_SYNC_FRAME : 0, > + }; > + > + err = OH_AVBuffer_SetBufferAttr(input->buffer, &attr); > + if (err != AV_ERR_OK) { > + ret = ff_oh_err_to_ff_err(err); > + return ret; > + } > + err = OH_VideoEncoder_PushInputBuffer(s->enc, input->index); > + if (err != AV_ERR_OK) { > + ret = ff_oh_err_to_ff_err(err); > + av_log(avctx, AV_LOG_ERROR, "Push input buffer failed, %d, %s\n", > + err, av_err2str(ret)); > + return ret; > + } > + av_frame_unref(s->frame); > + > + return 0; > +} > + > +static int oh_encode_receive(AVCodecContext *avctx, AVPacket *pkt) > +{ > + OHCodecEncContext *s = avctx->priv_data; > + > + while (1) { > + OHBufferQueueItem buffer = {0}; > + int ret; > + > + // Try get output > + ff_mutex_lock(&s->output_mutex); > + while (!s->encode_status) { > + if (av_fifo_read(s->output_queue, &buffer, 1) >= 0) > + break; > + // Only wait after send EOF > + if (s->eof_sent && !s->encode_status) > + ff_cond_wait(&s->output_cond, &s->output_mutex); > + else > + break; > + } > + > + ret = s->encode_status; > + ff_mutex_unlock(&s->output_mutex); > + > + // Got a packet > + if (buffer.buffer) > + return oh_encode_output_packet(avctx, pkt, &buffer); > + if (ret < 0) > + return ret; > + > + if (!s->frame->buf[0]) { > + /* fetch new frame or eof */ > + ret = ff_encode_get_frame(avctx, s->frame); > + if (ret < 0 && ret != AVERROR_EOF) > + return ret; > + } > + > + if (s->native_window) { > + ret = oh_encode_send_hw_frame(avctx); > + if (ret < 0) > + return ret; > + continue; > + } > + > + // Wait input buffer > + ff_mutex_lock(&s->input_mutex); > + while (!s->encode_status) { > + if (av_fifo_read(s->input_queue, &buffer, 1) >= 0) > + break; > + ff_cond_wait(&s->input_cond, &s->input_mutex); > + } > + > + ret = s->encode_status; > + ff_mutex_unlock(&s->input_mutex); > + > + if (ret < 0) > + return ret; > + > + ret = oh_encode_send_sw_frame(avctx, &buffer); > + if (ret < 0) > + return ret; > + } > + > + return AVERROR(EAGAIN); > +} > + > +static void oh_encode_flush(AVCodecContext *avctx) > +{ > + OHCodecEncContext *s = avctx->priv_data; > + > + OH_VideoEncoder_Flush(s->enc); > + > + ff_mutex_lock(&s->input_mutex); > + ff_mutex_lock(&s->output_mutex); > + av_fifo_reset2(s->input_queue); > + av_fifo_reset2(s->output_queue); > + s->encode_status = 0; > + s->eof_sent = false; > + ff_mutex_unlock(&s->output_mutex); > + ff_mutex_unlock(&s->input_mutex); > + > + OH_VideoEncoder_Start(s->enc); > +} > + > +static const AVCodecHWConfigInternal *const oh_hw_configs[] = { > + &(const AVCodecHWConfigInternal) { > + .public = { > + .pix_fmt = AV_PIX_FMT_OHCODEC, > + .methods = AV_CODEC_HW_CONFIG_METHOD_AD_HOC, > + }, > + .hwaccel = NULL, > + }, > + NULL > +}; > + > +static const FFCodecDefault ohcodec_defaults[] = { > + {"g", "-2"}, > + {NULL}, > +}; > + > +#define OFFSET(x) offsetof(OHCodecEncContext, x) > +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM > +static const AVOption ohcodec_venc_options[] = { > + {"codec_name", "Select codec by name", > + OFFSET(name), AV_OPT_TYPE_STRING, .flags = VE}, > + {"allow_sw", "Allow software encoding", > + OFFSET(allow_sw), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, VE}, > + {"native_window", "Export native window created by encoder", > + OFFSET(native_window), AV_OPT_TYPE_UINT64, .flags = VE | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY}, > + { "bitrate_mode", "Bitrate control method", > + OFFSET(bitrate_mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, .unit = "bitrate_mode" }, > + {"cbr", "Constant bitrate mode", > + 0, AV_OPT_TYPE_CONST, {.i64 = CBR}, 0, 0, VE, .unit = "bitrate_mode"}, > + {"vbr", "Variable bitrate mode", > + 0, AV_OPT_TYPE_CONST, {.i64 = VBR}, 0, 0, VE, .unit = "bitrate_mode"}, > + {"cq", "Constant quality mode", > + 0, AV_OPT_TYPE_CONST, {.i64 = CQ}, 0, 0, VE, .unit = "bitrate_mode"}, > + {NULL}, > +}; > + > +#define DECLARE_OHCODEC_CLASS(name) \ > +static const AVClass name ## _oh_enc_class = { \ > + .class_name = #name "_ohcodec", \ > + .item_name = av_default_item_name, \ > + .option = ohcodec_venc_options, \ > + .version = LIBAVUTIL_VERSION_INT, \ > +}; \ > + > +#define DECLARE_OHCODEC_ENCODER(short_name, long_name, codec_id) \ > +DECLARE_OHCODEC_CLASS(short_name) \ > +const FFCodec ff_ ## short_name ## _oh_encoder = { \ > + .p.name = #short_name "_ohcodec", \ > + CODEC_LONG_NAME(long_name " OpenHarmony Codec"), \ > + .p.type = AVMEDIA_TYPE_VIDEO, \ > + .p.id = codec_id, \ > + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | \ > + AV_CODEC_CAP_HARDWARE | \ > + AV_CODEC_CAP_ENCODER_FLUSH, \ > + .priv_data_size = sizeof(OHCodecEncContext), \ > + CODEC_PIXFMTS_ARRAY(ohcodec_pix_fmts), \ > + .color_ranges = AVCOL_RANGE_MPEG | AVCOL_RANGE_JPEG, \ > + .defaults = ohcodec_defaults, \ > + .init = oh_encode_init, \ > + FF_CODEC_RECEIVE_PACKET_CB(oh_encode_receive), \ > + .close = oh_encode_close, \ > + .flush = oh_encode_flush, \ > + .p.priv_class = &short_name ## _oh_enc_class, \ > + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, \ > + .p.wrapper_name = "ohcodec", \ > + .hw_configs = oh_hw_configs, \ > +}; \ > + > +#if CONFIG_H264_OH_ENCODER > +DECLARE_OHCODEC_ENCODER(h264, "H.264", AV_CODEC_ID_H264) > +#endif // CONFIG_H264_OH_ENCODER > + > +#if CONFIG_HEVC_OH_ENCODER > +DECLARE_OHCODEC_ENCODER(hevc, "H.265", AV_CODEC_ID_HEVC) > +#endif // CONFIG_HEVC_OH_ENCODER > + > diff --git a/libavcodec/version.h b/libavcodec/version.h > index da54f87887..7acb261bb3 100644 > --- a/libavcodec/version.h > +++ b/libavcodec/version.h > @@ -29,7 +29,7 @@ > > #include "version_major.h" > > -#define LIBAVCODEC_VERSION_MINOR 6 > +#define LIBAVCODEC_VERSION_MINOR 7 > #define LIBAVCODEC_VERSION_MICRO 100 > > #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ > -- > 2.46.0 > > _______________________________________________ > 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". _______________________________________________ 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".
next prev parent reply other threads:[~2025-07-06 9:02 UTC|newest] Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top [not found] <20250706080553.64116-1-quinkblack@foxmail.com> 2025-07-06 8:06 ` Zhao Zhili 2025-07-06 9:01 ` Zhao Zhili [this message] 2025-07-06 11:57 ` [FFmpeg-devel] [PATCH v3 " Zhao Zhili
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=tencent_039F44AB154E0B70432D579A0E40519AE008@qq.com \ --to=quinkblack-at-foxmail.com@ffmpeg.org \ --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