diff --git a/VisionSDK/Ascendffmpeg/README.md b/VisionSDK/Ascendffmpeg/README.md index 5b97a713f09dc94df8e05e9f2bdd3c5eabaf99be..a310c35117e2e0d49c45b019fd9c7ad78b768496 100644 --- a/VisionSDK/Ascendffmpeg/README.md +++ b/VisionSDK/Ascendffmpeg/README.md @@ -8,12 +8,12 @@ mxVison ascend 硬件平台内置了视频相关的硬件加速解码器,为 支持的功能: -|功能|mpeg4|h264/h265|多路| -|:----:|:----:|:----:|:----:| -|硬件解码|√|√|√| -|硬件编码|√|√|√| -|硬件转码|√|√|√| -|硬件缩放|√|√|√| +|功能|mpeg4|h264/h265| mjpeg |多路 | +|:----:|:----:|:----:|:-----:|:-------:| +|硬件解码|√|√| √ | √ | +|硬件编码|√|√| | √ | +|硬件转码|√|√| √ | √ | +|硬件缩放|√|√| | √ | ### 1.2 支持的产品 @@ -86,7 +86,7 @@ export LD_LIBRARY_PATH=/PATH/TO/mindxsdk-referenceapps/VisionSDK/Ascendffmpeg/as * `-hwaccel` - 指定采用 ascend 来进行硬件加速, 用来做硬件相关初始化工作。 解码相关参数(注意:解码相关参数需要在 `-i` 参数前设置): -* `-c:v` - 指定解码器为 h264_ascend (解码 h265 格式可以使用 h265_ascend)。 +* `-c:v` - 指定解码器为 h264_ascend (解码 h265 格式可以使用 h265_ascend,解码 mjpeg 格式可以使用 mjpeg_ascend)。 * `-device_id` - 指定硬件设备 id 为 0。取值范围取决于芯片个数,默认为 0。 `npu-smi info` 命令可以查看芯片个数 * `-channel_id` - 指定解码通道 id ,默认为0,取值范围取决于芯片实际情况,超出时会报错(对于昇腾Atlas 300I pro、 Atlas 300V pro,该参数的取值范围:[0, 256),JPEGD功能和VDEC功能共用通道,且通道总数最多256。对于Atlas 500 A2推理产品,该参数的取值范围:[0, 128),JPEGD功能和VDEC功能共用通道,且通道总数最多128)。 若是指定的通道已被占用, 则自动寻找并申请新的通道。 * `-resize` - 指定缩放大小, 输入格式为: {width}x{height}。宽高:[128x128-4096x4096], 宽高相乘不能超过 4096*2304(此为h264的约束)。宽要与 16 对齐,高要与 2 对齐。 @@ -120,7 +120,10 @@ export LD_LIBRARY_PATH=/PATH/TO/mindxsdk-referenceapps/VisionSDK/Ascendffmpeg/as # 将输入文件 out.yuv 编码为H.264格式,输出文件为 out.264 ./ffmpeg -hwaccel ascend -s 1920x1080 -pix_fmt nv12 -i out.yuv -c:v h264_ascend out.264 ``` - +```bash +# 将输入文件 test.mjpeg 通过 Ascend 硬件解码与编码,最终输出为 out.264 +./ffmpeg -hwaccel ascend -c:v mjpeg_ascend -device_id 0 -channel_id 0 -i test.mjpeg -c:v h264_ascend -device_id 0 -channel_id 0 out.264 +``` ## 4 常见问题 ### 4.1 文件编译不通过 diff --git a/VisionSDK/Ascendffmpeg/libavcodec/Makefile b/VisionSDK/Ascendffmpeg/libavcodec/Makefile index fc092e39fe121707c941f5d5b265c3192cc07936..f5c3835fb856d8a282b389ad39fc5b6018fb5c5d 100644 --- a/VisionSDK/Ascendffmpeg/libavcodec/Makefile +++ b/VisionSDK/Ascendffmpeg/libavcodec/Makefile @@ -27,6 +27,7 @@ HEADERS = ac3_parser.h \ xvmc.h \ ascend_dec.h \ ascend_enc.h \ + ascend_mjpeg_dec.h \ OBJS = ac3_parser.o \ adts_parser.o \ @@ -374,6 +375,7 @@ OBJS-$(CONFIG_H264_DECODER) += h264dec.o h264_cabac.o h264_cavlc.o \ h264_slice.o h264data.o OBJS-$(CONFIG_H264_ASCEND_DECODER) += ascend_dec.o OBJS-$(CONFIG_H264_ASCEND_ENCODER) += ascend_enc.o +OBJS-$(CONFIG_MJPEG_ASCEND_DECODER) += ascend_mjpeg_dec.o OBJS-$(CONFIG_H264_AMF_ENCODER) += amfenc_h264.o OBJS-$(CONFIG_H264_CUVID_DECODER) += cuviddec.o OBJS-$(CONFIG_H264_MEDIACODEC_DECODER) += mediacodecdec.o diff --git a/VisionSDK/Ascendffmpeg/libavcodec/allcodecs.c b/VisionSDK/Ascendffmpeg/libavcodec/allcodecs.c index b6885f8cda7a1017dbbc1c32221204af2ec7da46..1e2e0f3560a1332c051ca9748db0d878aa2fdaca 100644 --- a/VisionSDK/Ascendffmpeg/libavcodec/allcodecs.c +++ b/VisionSDK/Ascendffmpeg/libavcodec/allcodecs.c @@ -791,6 +791,7 @@ extern AVCodec ff_h264_ascend_decoder; extern AVCodec ff_h265_ascend_decoder; extern AVCodec ff_h264_ascend_encoder; extern AVCodec ff_h265_ascend_encoder; +extern AVCodec ff_mjpeg_ascend_decoder; extern AVCodec ff_h264_amf_encoder; extern AVCodec ff_h264_cuvid_decoder; extern AVCodec ff_h264_mf_encoder; diff --git a/VisionSDK/Ascendffmpeg/libavcodec/ascend_mjpeg_dec.c b/VisionSDK/Ascendffmpeg/libavcodec/ascend_mjpeg_dec.c new file mode 100644 index 0000000000000000000000000000000000000000..cdf69df74df7528dee48f9cf3ea3750cce60857b --- /dev/null +++ b/VisionSDK/Ascendffmpeg/libavcodec/ascend_mjpeg_dec.c @@ -0,0 +1,571 @@ +/* + * Copyright(c) 2024. Huawei Technologies Co.,Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except int compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "libavutil/imgutils.h" +#include "libavutil/avassert.h" +#include "libavutil/opt.h" +#include "avcodec.h" +#include "blockdsp.h" +#include "copy_block.h" +#include "decode.h" +#include "hwconfig.h" +#include "idctdsp.h" +#include "internal.h" +#include "jpegtables.h" +#include "jpeglsdec.h" +#include "profiles.h" +#include "put_bits.h" +#include "tiff.h" +#include "exif.h" +#include "bytestream.h" +#include "ascend_mjpeg_dec.h" + + +static const uint8_t jpeg_header_ascend[] = { + 0xff, 0xd8, // SOI + 0xff, 0xe0, // APP0 + 0x00, 0x10, // APP0 header size + 0x4a, 0x46, 0x49, 0x46, 0x00, // ID string 'JFIF\0' + 0x01, 0x01, // version + 0x00, // bits per type + 0x00, 0x00, // X density + 0x00, 0x00, // Y density + 0x00, // X thumbnail size + 0x00, // Y thumbnail size +}; + +static const int dht_segment_size_ascend = 420; +static const int bits_dc_luminance = 16; +static const int val_dc = 12; +static const int bits_ac_luminance = 16; +static const int val_ac_luminance = 162; +static const int bits_ac_chrominance = 16; +static const int val_ac_chrominance = 162; + +static const uint8_t dht_segment_head_ascend[] = {0xFF, 0xC4, 0x01, 0xA2, 0x00}; +static const uint8_t dht_segment_frag_ascend[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static uint8_t *append_ascend(uint8_t *buf, const uint8_t *src, int size) +{ + memcpy(buf, src, size); + return buf + size; +} + +static uint8_t *append_dht_segment_ascend(uint8_t *buf) +{ + buf = append_ascend(buf, dht_segment_head_ascend, sizeof(dht_segment_head_ascend)); + buf = append_ascend(buf, avpriv_mjpeg_bits_dc_luminance + 1, bits_dc_luminance); + buf = append_ascend(buf, dht_segment_frag_ascend, sizeof(dht_segment_frag_ascend)); + buf = append_ascend(buf, avpriv_mjpeg_val_dc, val_dc); + *(buf++) = 0x10; + buf = append_ascend(buf, avpriv_mjpeg_bits_ac_luminance + 1, bits_ac_luminance); + buf = append_ascend(buf, avpriv_mjpeg_val_ac_luminance, val_ac_luminance); + *(buf++) = 0x11; + buf = append_ascend(buf, avpriv_mjpeg_bits_ac_chrominance + 1, bits_ac_chrominance); + buf = append_ascend(buf, avpriv_mjpeg_val_ac_chrominance, val_ac_chrominance); + return buf; +} + +#define REF_FRAME_NUM 8 +#define DISPLAY_FRAME_NUM 2 +#define FFALIGNMJPEG(x, a) (((x) + (a) - 1) &~ ((a) - 1)) +#define WIDTH_ALIGN 2 +#define HEIGHT_ALIGN 16 +#define MAX_WIDTH 4096 +#define MAX_HEIGHT 4096 +#define MIN_WIDTH 128 +#define MIN_HEIGHT 128 +#define POOL_SIZE 2 +#define NUM_EIGHT 8 +#define NUM_FIVE 5 +#define NUM_FOUR 4 +#define NUM_THREE 3 +#define NUM_TWO 2 +#define MIN_PKT_SIZE 12 +#define SEND_STREAM_TIME_DELAY 1000 +#define GET_FRAME_TIME_DELAY 100 + + +av_cold int ff_mjpeg_ascend_decode_init(AVCodecContext* avctx) +{ + AscendMJpegDecodeContext *s = avctx->priv_data; + int ret; + + enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_ASCEND, AV_PIX_FMT_NV12, AV_PIX_FMT_NONE }; + avctx->pix_fmt = ff_get_format(avctx, pix_fmts); + if (avctx->pix_fmt != AV_PIX_FMT_ASCEND) { + av_log(avctx, AV_LOG_ERROR, "Perhaps the command \"-hwaccel ascend\" is missing. Please check it.\n"); + return AVERROR(EINVAL); + } + + if (avctx->width < MIN_WIDTH || avctx->height < MIN_HEIGHT || + avctx->width > MAX_WIDTH || avctx->height > MAX_HEIGHT) { + av_log(avctx, AV_LOG_ERROR, "MJPEG decoder only support resolution: 128x128 ~ 4096x4096, now: %dx%d.\n", + avctx->width, avctx->height); + return AVERROR(EINVAL); + } + + char device_id[sizeof(int)]; + sprintf(device_id, "%d", s->device_id); + + AVASCENDDeviceContext* hw_device_ctx; + AVHWFramesContext* hw_frames_ctx; + if (avctx->hw_frames_ctx) { + av_buffer_unref(&s->hw_frame_ref); + s->hw_frame_ref = av_buffer_ref(avctx->hw_frames_ctx); + if (!s->hw_frame_ref) { + ret = AVERROR(EINVAL); + return ret; + } + + hw_frames_ctx = (AVHWFramesContext*)s->hw_frame_ref->data; + if (!hw_frames_ctx->pool || (avctx->width != hw_frames_ctx->width)) { + if (hw_frames_ctx->pool) { + av_buffer_pool_uninit(&hw_frames_ctx->pool); + } + hw_frames_ctx->width = avctx->width; + hw_frames_ctx->height = avctx->height; + hw_frames_ctx->initial_pool_size = POOL_SIZE; + hw_frames_ctx->format = AV_PIX_FMT_ASCEND; + hw_frames_ctx->sw_format = AV_PIX_FMT_NV12; + + ret = av_hwframe_ctx_init(s->hw_frame_ref); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "HWFrame context init failed, ret is %d.\n", ret); + return AVERROR(ENAVAIL); + } + } + s->hw_device_ref = av_buffer_ref(hw_frames_ctx->device_ref); + if (!s->hw_device_ref) { + av_log(avctx, AV_LOG_ERROR, "Get hw_device_ref failed.\n"); + ret = AVERROR(EINVAL); + return ret; + } + } else { + if (avctx->hw_device_ctx) { + s->hw_device_ref = av_buffer_ref(avctx->hw_device_ctx); + if (!s->hw_device_ref) { + av_log(avctx, AV_LOG_ERROR, "ref hwdevice failed.\n"); + ret = AVERROR(EINVAL); + return ret; + } + } else { + ret = av_hwdevice_ctx_create(&s->hw_device_ref, AV_HWDEVICE_TYPE_ASCEND, device_id, NULL, 0); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "hwdevice context create failed. ret is %d.\n", ret); + return ret; + } + } + s->hw_frame_ref = av_hwframe_ctx_alloc(s->hw_device_ref); + if (!s->hw_frame_ref) { + av_log(avctx, AV_LOG_ERROR, "hwframe ctx alloc falied.\n"); + ret = AVERROR(EINVAL); + return ret; + } + hw_frames_ctx = (AVHWFramesContext*)s->hw_frame_ref->data; + if (!hw_frames_ctx->pool) { + hw_frames_ctx->width = avctx->width; + hw_frames_ctx->height = avctx->height; + hw_frames_ctx->initial_pool_size = POOL_SIZE; + hw_frames_ctx->format = AV_PIX_FMT_ASCEND; + hw_frames_ctx->sw_format = AV_PIX_FMT_NV12; + ret = av_hwframe_ctx_init(s->hw_frame_ref); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "hwframe ctx init error, ret is %d.\n", ret); + ret = AVERROR(EINVAL); + return ret; + } + } + } + + hw_device_ctx = ((AVHWDeviceContext*)s->hw_device_ref->data)->hwctx; + s->hw_device_ctx = hw_device_ctx; + s->hw_frames_ctx = hw_frames_ctx; + s->ascend_ctx = s->hw_device_ctx->ascend_ctx; + + ret = aclrtSetCurrentContext(s->ascend_ctx->context); + if (ret != 0) { + av_log(avctx, + AV_LOG_ERROR, "Set context failed at line(%d) in func(%s), ret is %d.\n", __LINE__, __func__, ret); + return ret; + } + + ret = hi_mpi_sys_init(); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "HiMpi sys init failed, ret is %d.\n", ret); + return ret; + } + + hi_vdec_chn_attr chn_attr_; + chn_attr_.type = HI_PT_JPEG; + chn_attr_.mode = HI_VDEC_SEND_MODE_FRAME; + chn_attr_.pic_width = avctx->width; + chn_attr_.pic_height = avctx->height; + chn_attr_.stream_buf_size = chn_attr_.pic_width * chn_attr_.pic_height * + NUM_THREE / NUM_TWO; + chn_attr_.frame_buf_cnt = REF_FRAME_NUM + DISPLAY_FRAME_NUM + 1; + + hi_pic_buf_attr buf_attr_; + buf_attr_.width = chn_attr_.pic_width; + buf_attr_.height = chn_attr_.pic_height; + buf_attr_.align = 0; + buf_attr_.bit_width = HI_DATA_BIT_WIDTH_8; + buf_attr_.pixel_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420; + buf_attr_.compress_mode = HI_COMPRESS_MODE_NONE; + + chn_attr_.frame_buf_size = hi_vdec_get_pic_buf_size(chn_attr_.type, &buf_attr_); + chn_attr_.video_attr.ref_frame_num = REF_FRAME_NUM; + chn_attr_.video_attr.temporal_mvp_en = HI_TRUE; + chn_attr_.video_attr.tmv_buf_size = hi_vdec_get_tmv_buf_size(chn_attr_.type, + chn_attr_.pic_width, + chn_attr_.pic_height); + uint32_t channel_id = s->channel_id; + ret = hi_mpi_vdec_create_chn(channel_id, &chn_attr_); + if (ret != 0) { + hi_mpi_sys_exit(); + av_log(avctx, AV_LOG_ERROR, "HiMpi create vdec channel failed, ret is %d.\n", ret); + return ret; + } + + hi_vdec_chn_param chn_param_; + ret = hi_mpi_vdec_get_chn_param(channel_id, &chn_param_); + if (ret != 0) { + hi_mpi_vdec_destroy_chn(s->channel_id); + hi_mpi_sys_exit(); + av_log(avctx, AV_LOG_ERROR, "HiMpi vdec get channel param failed, ret is %d.\n", ret); + return ret; + } + + chn_param_.video_param.dec_mode = HI_VIDEO_DEC_MODE_IPB; + chn_param_.video_param.compress_mode = HI_COMPRESS_MODE_HFBC; + chn_param_.video_param.video_format = HI_VIDEO_FORMAT_TILE_64x16; + chn_param_.display_frame_num = DISPLAY_FRAME_NUM; + chn_param_.video_param.out_order = HI_VIDEO_OUT_ORDER_DISPLAY; + + ret = hi_mpi_vdec_set_chn_param(channel_id, &chn_param_); + if (ret != 0) { + hi_mpi_vdec_destroy_chn(s->channel_id); + hi_mpi_sys_exit(); + av_log(avctx, AV_LOG_ERROR, "HiMpi vdec set channel param failed, ret is %d.\n", ret); + return ret; + } + + ret = hi_mpi_vdec_start_recv_stream(channel_id); + if (ret != 0) { + hi_mpi_vdec_stop_recv_stream(s->channel_id); + hi_mpi_vdec_destroy_chn(s->channel_id); + hi_mpi_sys_exit(); + av_log(avctx, AV_LOG_ERROR, "HiMpi vdec start receive stream failed, ret is %d.\n", ret); + return ret; + } + + s->pkt = av_packet_alloc(); + if (!s->pkt) { + hi_mpi_vdec_stop_recv_stream(s->channel_id); + hi_mpi_vdec_destroy_chn(s->channel_id); + hi_mpi_sys_exit(); + av_log(avctx, AV_LOG_ERROR, "Init packet failed, ret is %d.\n", ret); + return AVERROR(ENOMEM); + } + s->avctx = avctx; + return 0; +} + +static int mjpeg_get_packet(AVCodecContext* avctx) +{ + AscendMJpegDecodeContext* s = avctx->priv_data; + int ret; + av_packet_unref(s->pkt); + ret = ff_decode_get_packet(avctx, s->pkt); + if (ret < 0) { + return ret; + } + s->buf_size = s->pkt->size; + return 0; +} + +int ff_mjpeg_ascend_receive_frame(AVCodecContext* avctx, AVFrame* frame) +{ + AscendMJpegDecodeContext *s = avctx->priv_data; + int ret = 0; + ret = mjpeg_get_packet(avctx); + if (ret < 0) { + return ret; + } + + /* GET JPEG */ + AVPacket *out = av_packet_alloc(); + uint8_t* output; + int input_skip, output_size; + AVPacket *in = s->pkt; + + if (in->size < MIN_PKT_SIZE) { + av_log(avctx, AV_LOG_ERROR, "Input is truncate.\n"); + return AVERROR_INVALIDDATA; + } + + if (AV_RB16(in->data) != 0xffd8) { + av_log(avctx, AV_LOG_ERROR, "Input is not MJPEG.\n"); + return AVERROR_INVALIDDATA; + } + + if (in->data[NUM_TWO] == 0xff && in->data[NUM_THREE] == APP0) { + input_skip = (in->data[NUM_FOUR] << NUM_EIGHT) + in->data[NUM_FIVE] + NUM_FOUR; + } else { + input_skip = NUM_TWO; + } + + if (in->size < input_skip) { + av_log(avctx, AV_LOG_ERROR, "Input is truncate.\n"); + return AVERROR_INVALIDDATA; + } + + output_size = in->size - input_skip + sizeof(jpeg_header_ascend) + dht_segment_size_ascend; + ret = av_new_packet(out, output_size); + if (ret < 0) + return AVERROR_INVALIDDATA; + output = out->data; + output = append_ascend(output, jpeg_header_ascend, sizeof(jpeg_header_ascend)); + output = append_dht_segment_ascend(output); + output = append_ascend(output, in->data + input_skip, in->size - input_skip); + + /* JPEG to YUV420 By Ascend */ + ret = aclrtSetCurrentContext(s->ascend_ctx->context); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "Set context failed, ret is %d.\n", ret); + goto error; + } + + uint8_t* streamBuffer = NULL; + int device_id = 0; + ret = hi_mpi_dvpp_malloc(device_id, &streamBuffer, output_size); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "HiMpi malloc packet failed, ret is %d.\n", ret); + goto error; + } + + ret = aclrtMemcpy(streamBuffer, output_size, out->data, output_size, ACL_MEMCPY_HOST_TO_DEVICE); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "Mem copy H2D failed. ret is %d.\n", ret); + hi_mpi_dvpp_free(streamBuffer); + goto error; + } + + hi_vdec_stream stream; + stream.pts = 0; + stream.addr = streamBuffer; + stream.len = output_size; + stream.end_of_frame = HI_TRUE; + stream.end_of_stream = HI_FALSE; + stream.need_display = HI_TRUE; + + hi_vdec_pic_info pic_info; + pic_info.width = s->avctx->width; + pic_info.height = s->avctx->height; + pic_info.width_stride = FFALIGNMJPEG(pic_info.width, WIDTH_ALIGN); + pic_info.height_stride = FFALIGNMJPEG(pic_info.height, HEIGHT_ALIGN); + pic_info.offset_top = 0; + pic_info.offset_bottom = 0; + pic_info.offset_left = 0; + pic_info.offset_right = 0; + + uint32_t size = pic_info.width_stride * pic_info.height_stride * NUM_THREE / NUM_TWO; + pic_info.buffer_size = size; + pic_info.pixel_format = HI_PIXEL_FORMAT_YUV_SEMIPLANAR_420; + + void* picBuffer = NULL; + ret = hi_mpi_dvpp_malloc(device_id, &picBuffer, size); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "HiMpi malloc falied, ret is %d.\n", ret); + hi_mpi_dvpp_free(streamBuffer); + goto error; + } + + pic_info.vir_addr = (uint64_t)picBuffer; + + ret = hi_mpi_vdec_send_stream(s->channel_id, &stream, &pic_info, SEND_STREAM_TIME_DELAY); + if (ret != 0) { + hi_mpi_dvpp_free(picBuffer); + hi_mpi_dvpp_free(streamBuffer); + av_log(avctx, AV_LOG_ERROR, "Send stream failed, ret is %d.\n", ret); + goto error; + } + + hi_video_frame_info got_frame; + hi_vdec_stream got_stream; + hi_vdec_supplement_info stSupplement; + ret = hi_mpi_vdec_get_frame(s->channel_id, &got_frame, &stSupplement, &got_stream, GET_FRAME_TIME_DELAY); + if (ret != 0) { + hi_mpi_dvpp_free(picBuffer); + hi_mpi_dvpp_free(streamBuffer); + av_log(avctx, AV_LOG_ERROR, "Get frame failed, ret is %d.\n", ret); + goto error; + } + size_t decResult = got_frame.v_frame.frame_flag; + hi_mpi_dvpp_free(got_stream.addr); // free decode input + if (decResult != 0 && got_frame.v_frame.virt_addr[0] != NULL) { + hi_mpi_dvpp_free(got_frame.v_frame.virt_addr[0]); + goto error; + } + if (decResult != 0 || got_frame.v_frame.virt_addr[0] == NULL || got_stream.need_display == HI_FALSE) { + hi_mpi_dvpp_free(got_frame.v_frame.virt_addr[0]); + ret = hi_mpi_vdec_release_frame(s->channel_id, &got_frame); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "HiMpi release frame failed, ret is %d.\n", ret); + goto error; + } + ret = -1; + goto error; + } + ret = hi_mpi_vdec_release_frame(s->channel_id, &got_frame); + if (ret != 0) { + hi_mpi_dvpp_free(got_frame.v_frame.virt_addr[0]); + av_log(avctx, AV_LOG_ERROR, "HiMpi release frame failed, ret is %d.\n", ret); + goto error; + } + + ret = av_hwframe_get_buffer(s->hw_frame_ref, frame, 0); + if (ret < 0) { + hi_mpi_dvpp_free(got_frame.v_frame.virt_addr[0]); + av_log(avctx, AV_LOG_ERROR, "Frame get buffer failed, ret is %d.\n", ret); + goto error; + } + ret = ff_decode_frame_props(avctx, frame); + if (ret < 0) { + hi_mpi_dvpp_free(got_frame.v_frame.virt_addr[0]); + av_log(avctx, AV_LOG_ERROR, "Fill frame properties failed. ret is %d.\n", ret); + goto error; + } + + frame->pkt_pos = -1; + frame->pkt_duration = 0; + frame->pkt_size = pic_info.buffer_size; + frame->width = got_frame.v_frame.width_stride[0]; + frame->height = got_frame.v_frame.height_stride[0]; + frame->format = (int)AV_PIX_FMT_NV12; + frame->pkt_dts = s->pkt->dts; + + uint32_t offset = 0; + for (int i = 0; i < NUM_TWO; i++) { + size_t dstBytes = got_frame.v_frame.width_stride[0] * got_frame.v_frame.height_stride[0] * (i ? 1.0 / 2 : 1); + ret = aclrtMemcpy(frame->data[i], dstBytes, got_frame.v_frame.virt_addr[0] + offset, dstBytes, + ACL_MEMCPY_DEVICE_TO_DEVICE); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "Mem copy D2D failed, ret is %d.\n", ret); + hi_mpi_dvpp_free(got_frame.v_frame.virt_addr[0]); + goto error; + } + offset += dstBytes; + } + hi_mpi_dvpp_free(got_frame.v_frame.virt_addr[0]); + error: + av_packet_free(&out); + return ret; +} + +av_cold int ff_mjpeg_ascend_decode_end(AVCodecContext* avctx) +{ + AscendMJpegDecodeContext *s = avctx->priv_data; + int ret; + ret = aclrtSetCurrentContext(s->ascend_ctx->context); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "Set context failed, ret is %d.\n", ret); + return ret; + } + + ret = hi_mpi_vdec_stop_recv_stream(s->channel_id); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "HiMpi stop receive stream failed, ret is %d.\n", ret); + return ret; + } + + ret = hi_mpi_vdec_destroy_chn(s->channel_id); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "HiMpi destroy channel failed, ret is %d.\n", ret); + return ret; + } + + ret = hi_mpi_sys_exit(); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "HiMpi sys exit failed, ret is %d.\n", ret); + return ret; + } + + av_packet_free(&s->pkt); + av_buffer_unref(&s->hw_device_ref); + return 0; +} + +static void ascend_decode_flush(AVCodecContext* avctx) +{ + ff_mjpeg_ascend_decode_end(avctx); + ff_mjpeg_ascend_decode_init(avctx); +} + +#define OFFSET(x) offsetof(AscendMJpegDecodeContext, x) +#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + { "device_id", "Use to choose the ascend chip.", OFFSET(device_id), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 8, VD }, + { "channel_id", "Set channelId of decoder.", OFFSET(channel_id), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 255, VD }, + { NULL } +}; + + +static const AVCodecHWConfigInternal* ascend_hw_configs[] = { + &(const AVCodecHWConfigInternal) { + .public = { + .pix_fmt = AV_PIX_FMT_ASCEND, + .methods = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX | AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX | + AV_CODEC_HW_CONFIG_METHOD_INTERNAL, + .device_type = AV_HWDEVICE_TYPE_ASCEND + }, + .hwaccel = NULL, + }, + NULL +}; + +#define ASCEND_DEC_CODEC(x, X) \ + static const AVClass x##_ascend_class = { \ + .class_name = #x "_ascend_dec", \ + .item_name = av_default_item_name, \ + .option = options, \ + .version = LIBAVUTIL_VERSION_INT, \ + }; \ + AVCodec ff_##x##_ascend_decoder = { \ + .name = #x "_ascend", \ + .long_name = NULL_IF_CONFIG_SMALL("Ascend HiMpi " #X " decoder"), \ + .type = AVMEDIA_TYPE_VIDEO, \ + .id = AV_CODEC_ID_MJPEG, \ + .priv_data_size = sizeof(AscendMJpegDecodeContext), \ + .priv_class = &x##_ascend_class, \ + .init = ff_mjpeg_ascend_decode_init, \ + .close = ff_mjpeg_ascend_decode_end, \ + .receive_frame = ff_mjpeg_ascend_receive_frame, \ + .flush = ascend_decode_flush, \ + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \ + .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_ASCEND, \ + AV_PIX_FMT_NV12, \ + AV_PIX_FMT_NONE }, \ + .hw_configs = ascend_hw_configs, \ + .wrapper_name = "ascendmjpegdec", \ + }; +#if CONFIG_MJPEG_ASCEND_DECODER +ASCEND_DEC_CODEC(mjpeg, MJPEG) +#endif \ No newline at end of file diff --git a/VisionSDK/Ascendffmpeg/libavcodec/ascend_mjpeg_dec.h b/VisionSDK/Ascendffmpeg/libavcodec/ascend_mjpeg_dec.h new file mode 100644 index 0000000000000000000000000000000000000000..087ca37cbad92278e642a4a88f1619e59461269d --- /dev/null +++ b/VisionSDK/Ascendffmpeg/libavcodec/ascend_mjpeg_dec.h @@ -0,0 +1,63 @@ +/* + * Copyright(c) 2025. Huawei Technologies Co.,Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except int compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * MJPEG Ascend decoder. + */ + +#ifndef ASCEND_AVCODEC_MJPEGDEC_H +#define ASCEND_AVCODEC_MJPEGDEC_H + +#include "libavutil/log.h" +#include "libavutil/mem_internal.h" +#include "libavutil/pixdesc.h" +#include "libavutil/stereo3d.h" + +#include "avcodec.h" +#include "blockdsp.h" +#include "get_bits.h" +#include "hpeldsp.h" +#include "idctdsp.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_ascend.h" +#include "acl/dvpp/hi_dvpp.h" + +typedef struct AscendMJpegDecodeContext { + AVClass *class; + AVCodecContext *avctx; + int buf_size; + + AVPacket *pkt; + enum AVPixelFormat hwaccel_sw_pix_fmt; + enum AVPixelFormat hwaccel_pix_fmt; + void* hwaccel_picture_private; + int device_id; + uint32_t channel_id; + uint32_t vdec_width; + uint32_t vdec_height; + AVBufferRef* hw_device_ref; + AVBufferRef* hw_frame_ref; + AVASCENDDeviceContext *hw_device_ctx; + AVHWFramesContext* hw_frames_ctx; + AscendContext* ascend_ctx; +} AscendMJpegDecodeContext; + +int ff_mjpeg_ascend_decode_init(AVCodecContext *avctx); +int ff_mjpeg_ascend_decode_end(AVCodecContext *avctx); +int ff_mjpeg_ascend_receive_frame(AVCodecContext *avctx, AVFrame *frame); + +#endif