diff --git a/VisionSDK/Ascendffmpeg/README_DEV.md b/VisionSDK/Ascendffmpeg/README_DEV.md new file mode 100644 index 0000000000000000000000000000000000000000..bda0525eff6eccd4623c981d2fa220230999b9b1 --- /dev/null +++ b/VisionSDK/Ascendffmpeg/README_DEV.md @@ -0,0 +1,140 @@ +## 1 介绍 + +### 1.1 简介 + +Vison ascend 硬件平台内置了视频相关的硬件加速编解码器,为了提升用户的易用性,Vision SDK提供了 Ffmepg-Ascend 解决方案。 + +支持的功能: + +|功能|mpeg4|h264/h265| mjpeg |多路 | +|:----:|:----:|:----:|:-----:|:-------:| +|硬件解码|√|√| √ | √ | +|硬件编码|√|√| | √ | +|硬件转码|√|√| √ | √ | +|硬件缩放|√|√| | √ | + +注意:mjpeg视频流的转码相关功能只在Atlas A500 A2上适用。 + +### 1.2 支持的产品 + +本项目支持昇腾Atlas 300I pro、 Atlas 300V pro、 Atlas A500 A2。 + +### 1.3 支持的版本 +本样例配套的CANN版本、Driver/Firmware版本如下所示: + +| CANN版本 | Driver/Firmware版本 | +| ------------------ | -------------- | +| 8.0.RC3 | 24.1.RC3 | + + +## 2 设置环境变量 +* `ASCEND_HOME` Ascend 安装的路径,一般为 `/usr/local/Ascend` +* 执行命令 + ```bash + export ASCEND_HOME=/usr/local/Ascend + . /usr/local/Ascend/ascend-toolkit/set_env.sh #toolkit默认安装路径,根据实际安装路径修改 + ``` + + +## 3 编译 + +**步骤1:** 在项目目录`Ascendffmpeg/`下添加可执行权限: +```bash +chmod +x ./configure +chmod +x ./ffbuild/*.sh +``` + +**步骤2:** 在项目目录`Ascendffmpeg/`下执行编译: + +编译选项说明: +* `prefix` - FFmpeg 及相关组件安装目录 +* `enable-shared` - FFmpeg 允许生成 so 文件 +* `extra-cflags` - 添加第三方头文件 +* `extra-ldflags` - 指定第三方库位置 +* `extra-libs` - 添加第三方 so 文件 +* `enable-ascend` - 允许使用 ascend 进行硬件加速 + +执行编译命令: + ```bash + ./configure \ + --prefix=./ascend \ + --enable-shared \ + --extra-cflags="-I${ASCEND_HOME}/ascend-toolkit/latest/acllib/include" \ + --extra-ldflags="-L${ASCEND_HOME}/ascend-toolkit/latest/acllib/lib64" \ + --extra-libs="-lacl_dvpp_mpi -lascendcl" \ + --enable-ascend \ + && make -j && make install + ``` + +**步骤3:** 添加环境变量 + +通过指令`find / -name libavdevice.so`查找到文件所在路径,形如`/PATH/TO/mindsdk-referenceapps/VisionSDK/Ascendffmpeg/ascend/lib/libavdevice.so`,则执行: +```bash +export LD_LIBRARY_PATH=/PATH/TO/mindsdk-referenceapps/VisionSDK/Ascendffmpeg/ascend/lib:$LD_LIBRARY_PATH +``` + + + +## 4 特性介绍 + +Ascendffmpeg在ffmpeg开源软件基础上,结合昇腾NPU设备硬件加速,扩充了视频编解码能力。 + +### 4.1 解码 + + + + + + + + + + + + + + + + + + + + + + + +
解码器介绍
h264_ascendlink
h265_ascendlink
mjpeg_ascendlink
+ + +### 4.2 编码 + + + + + + + + + + + + + + + + + + + +
编码器介绍
h264_ascendlink
h265_ascendlink
+ + +## 5 常见问题 +### 5.1 文件编译不通过 + +问题描述: 文件编译不通过 + +解决方案: 可能是文件格式被改变或者破坏,建议通过以下两种方式直接获取代码,而非文件传输: +- 在环境上通过git clone直接下载该代码仓。 +- 直接从代码仓网页gitee下载zip包,并在环境上通过`unzip`解压。 + diff --git a/VisionSDK/Ascendffmpeg/doc/dec_h26x_ascend.md b/VisionSDK/Ascendffmpeg/doc/dec_h26x_ascend.md new file mode 100644 index 0000000000000000000000000000000000000000..b175698189de9635a1475c54b00c28a9a2c68e45 --- /dev/null +++ b/VisionSDK/Ascendffmpeg/doc/dec_h26x_ascend.md @@ -0,0 +1,81 @@ +## h26x_ascend解码器 + +### 1 简介 + +Ffmepg-Ascend 中内置了h264_ascend和h265_ascend解码器,利用昇腾NPU设备分别处理h264视频流和h265视频流的解码。 + +### 2 头文件 + +```commandline +#include "libavcodec/ascend_dec.h" +``` + +### 3 特性支持 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
特性参数名类型说明h264_ascendh265_ascend
指定运行设备device_idint 取值范围取决于芯片个数,默认为 0。 `npu-smi info` 命令可以查看芯片个数
指定运行通道号channel_idint 取值范围取决于芯片实际情况,超出时会报错(对于昇腾Atlas 300I pro、 Atlas 300V pro,该参数的取值范围:[0, 256),JPEGD功能和VDEC功能共用通道,且通道总数最多256。对于Atlas 500 A2推理产品,该参数的取值范围:[0, 128),JPEGD功能和VDEC功能共用通道,且通道总数最多128)。 若是指定的通道已被占用, 则自动寻找并申请新的通道。
缩放resize_strchar* 输入格式为: {width}x{height}, 宽高:[128x128-4096x4096]
+ +### 4 解码器使用 + +开发态使用解码器代码样例请参考:hw_decode.c + +**特别的**: + +解码器默认在“0”号设备上用编号“0”通道进行解码操作,且默认不指定缩放尺寸。 + +如用户对执行设备,执行通道以及是否缩放有自定义诉求,可以利用“ascend_dec.h”文件中的ASCENDContext_t结构体实现,如下代码所示: + +```commandline + +ASCENDContext_t* privData = (ASCENDContext_t*)av_malloc(sizeof(ASCENDContext_t)); +privData->device_id = 1; // 用户可自定义修改 +privData->channel_id = 1; // 用户可自定义修改 +privData->resize_str = "1920x1080"; // 用户可自定义修改 + +AVCodec *decoder; // 假设已经完成对decoder的初始化 + +AVCodecContext* decoder_ctx = avcodec_alloc_context3(decoder); + +decoder_ctx->priv_data = privData; // 将自定义数据传入解码器上下文 + +/* 执行解码以及相关资源释放 */ +··· +``` diff --git a/VisionSDK/Ascendffmpeg/doc/dec_mjpeg_ascend.md b/VisionSDK/Ascendffmpeg/doc/dec_mjpeg_ascend.md new file mode 100644 index 0000000000000000000000000000000000000000..77ff989d0eeb27c5b4a7cc012712d269ac2ff60e --- /dev/null +++ b/VisionSDK/Ascendffmpeg/doc/dec_mjpeg_ascend.md @@ -0,0 +1,64 @@ +## mjpeg_ascend解码器 + +### 1 简介 + +Ffmepg-Ascend 中内置了mjpeg_ascend解码器,利用昇腾NPU设备处理mjpeg视频流的解码。当前该解码器仅支持在Atlas A500 A2上使用。 + +### 2 头文件 + +```commandline +#include "libavcodec/ascend_mjpeg_dec.h" +``` + +### 3 特性支持 + + + + + + + + + + + + + + + + + + + + + + + + + + + +
特性参数名类型说明mjpeg_ascend
指定运行设备device_idint 取值范围取决于芯片个数,默认为 0。 `npu-smi info` 命令可以查看芯片个数
指定运行通道号channel_idint 取值范围取决于芯片实际情况,超出时会报错(对于昇腾Atlas 300I pro、 Atlas 300V pro,该参数的取值范围:[0, 256),JPEGD功能和VDEC功能共用通道,且通道总数最多256。对于Atlas 500 A2推理产品,该参数的取值范围:[0, 128),JPEGD功能和VDEC功能共用通道,且通道总数最多128)。 若是指定的通道已被占用, 则自动寻找并申请新的通道。
+ +### 4 解码器使用 + +开发态使用解码器代码样例请参考:hw_decode.c + + +如用户对执行设备,执行通道有自定义诉求,可以利用“ascend_mjpeg_dec.h”文件中的AscendMJpegDecodeContext结构体实现,如下代码所示: + +```commandline + +AscendMJpegDecodeContext* privData = (AscendMJpegDecodeContext*)av_malloc(sizeof(AscendMJpegDecodeContext)); +privData->device_id = 1; // 用户可自定义修改 +privData->channel_id = 1; // 用户可自定义修改 + +AVCodec *decoder; // 假设已经完成对decoder的初始化 + +AVCodecContext* decoder_ctx = avcodec_alloc_context3(decoder); + +decoder_ctx->priv_data = privData; // 将自定义数据传入解码器上下文 + +/* 执行解码以及相关资源释放 */ +··· +``` diff --git a/VisionSDK/Ascendffmpeg/doc/enc_h26x_ascend.md b/VisionSDK/Ascendffmpeg/doc/enc_h26x_ascend.md new file mode 100644 index 0000000000000000000000000000000000000000..eca99206034d5e492448fa093717cea7a0ef4c0b --- /dev/null +++ b/VisionSDK/Ascendffmpeg/doc/enc_h26x_ascend.md @@ -0,0 +1,177 @@ +## h26x_ascend编码器 + +### 1 简介 + +Ffmepg-Ascend 中内置了h264_ascend和h265_ascend编码器,利用昇腾NPU设备分别处理h264视频流和h265视频流的编码。 + +### 2 头文件 + +```commandline +#include "libavcodec/ascend_enc.h" +``` + +### 3 特性支持 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
特性参数名类型说明h264_ascendh265_ascend
指定运行设备device_idint 取值范围取决于芯片个数,默认为 0。 `npu-smi info` 命令可以查看芯片个数
指定运行通道号channel_idint 取值范围取决于芯片实际情况,超出时会报错(对于昇腾Atlas 300I pro、 Atlas 300V pro,该参数的取值范围:[0, 256),JPEGD功能和VDEC功能共用通道,且通道总数最多256。对于Atlas 500 A2推理产品,该参数的取值范围:[0, 128),JPEGD功能和VDEC功能共用通道,且通道总数最多128)。 若是指定的通道已被占用, 则自动寻找并申请新的通道。
指定视频编码的画质级别profileint 0: baseline, 1: main, 2: high, 默认为 1。 H265 编码器只支持 main
指定视频编码器的速率控制模式 rc_modeint 0: CBR, 1: VBR, 默认为 0
指定关键帧间隔gopint[1, 65536], 默认为 30
指定帧率frame_rateint [1, 240], 默认为25
限制码流的最大比特率max_bit_rateint [2, 614400], 默认为 20000
指定视频场景movement_sceneint 0:静态场景(监控视频等), 1:动态场景(直播,游戏等), 默认为 1
强制编码下一帧为I帧\\ 该特性需要修改AVFrame中的opaque参数,详情见4.2
+ +### 4 编码器使用 + +开发态使用编码器代码样例请参考:encode_video.c + + + +#### 4.1 常规特性使用 + +如用户对执行设备,执行通道以及其他常规特性有自定义诉求,可以利用“ascend_enc.h”文件中的ASCENDEncContext_t结构体实现,如下代码所示: + +```commandline + +ASCENDEncContext_t* privData = (ASCENDEncContext_t*)av_malloc(sizeof(ASCENDEncContext_t)); +privData->device_id = 1; // 用户可自定义修改 +privData->channel_id = 1; // 用户可自定义修改 +privData->profile = 0; // 用户可自定义修改 +privData->rc_mode = 0; // 用户可自定义修改 +privData->gop = 25; // 用户可自定义修改 +privData->frame_rate = 25; // 用户可自定义修改 +privData->movement_scene = 0; // 用户可自定义修改 + +AVCodec *encoder; // 假设已经完成对encoder的初始化 + +AVCodecContext* encoder_ctx = avcodec_alloc_context3(encoder); + +decoder_ctx->priv_data = privData; // 将自定义数据传入编码器上下文 + +/* 执行编码以及相关资源释放 */ +··· +``` + +#### 4.2 强制编码下一帧为I帧 + +该特性通过AVFrame结构体中的opaque指针传递给编码器,如下代码所示: + +```commandline +// 通过AscendEncPrivateData_t来承载是否编码下一帧为I的语义。AscendEncPrivateData_t结构体定义在“liavcodec/avcodec.h”文件中。 +AscendEncPrivateData_t privData = (AscendEncPrivateData_t*)av_malloc(sizeof(AscendEncPrivateData_t)); +privData->next_is_I_frame = true; // 该参数为true时,代表下一帧将会强制编码为I帧 +privData->is_instant = true; // 该参数为true时,立即编出I帧,不受帧率控制约束;该参数为false时,则在帧率控制的下一帧编出I帧。 + // 当前底层支持场景为目标帧率与原帧率一致,故该参数目前无论设置为true或者false,都是每调用一次接口即可编出一个I帧,调用频繁会影响码流帧率和码率的稳定。 + +AVCodecContext* enc_ctx; // 假设已完成编码器上下文的初始化 +AVFrame *frame; // 假设已完成AVFrame的初始化 + +frame->opaque = privData; // 将是否将下一帧编码为I帧的信息赋值给frame + +avcodec_send_frame(enc_ctx, frame); // 调用编码器进行编码 + +/* 获取编码结果 */ +··· +frame->opaque = NULL; // 当编码I帧结束,需要对该参数进行复位,否则后续视频帧都会编码为I帧 + +/* 释放资源 */ +··· +``` diff --git a/VisionSDK/Ascendffmpeg/libavcodec/ascend_enc.c b/VisionSDK/Ascendffmpeg/libavcodec/ascend_enc.c index 7f091358aca75070a09486bfdc25d77a2b0ef84f..9bb32bce3d8308fa1b918369c998e4fdee13a358 100644 --- a/VisionSDK/Ascendffmpeg/libavcodec/ascend_enc.c +++ b/VisionSDK/Ascendffmpeg/libavcodec/ascend_enc.c @@ -589,7 +589,7 @@ static int hi_mpi_encode(ASCENDEncContext_t *ctx, const AVFrame *frame) { av_log(ctx, AV_LOG_DEBUG, "Send frame size: %ux%u, pts:%ld, frame type:%d.\n", frame->width, frame->height, frame->pts, frame->pict_type); - + hi_venc_start_param recvParam; recvParam.recv_pic_num = -1; int ret = hi_mpi_venc_start_chn(ctx->channel_id, &recvParam); @@ -746,6 +746,15 @@ static int ff_himpi_enc_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) continue; } + AscendEncPrivateData_t* priv_data = frame.opaque; + if (priv_data && priv_data->next_is_I_frame) { + ret = hi_mpi_venc_request_idr(ctx->channel_id, priv_data->is_instant); + if (ret) { + av_log(ctx, AV_LOG_ERROR, "Set I-frame failed. ret is %d.\n", ret); + return ret; + } + } + send_ret = hi_mpi_encode(ctx, &frame); av_frame_unref(&frame); if (send_ret < 0 && send_ret != AVERROR_EOF) { diff --git a/VisionSDK/Ascendffmpeg/libavcodec/avcodec.h b/VisionSDK/Ascendffmpeg/libavcodec/avcodec.h index 8a71c042308e37a89dbf357d8f9b276dd4ebcbf9..0cd9750a7404426f78b7678ba5df1086a91a68dd 100644 --- a/VisionSDK/Ascendffmpeg/libavcodec/avcodec.h +++ b/VisionSDK/Ascendffmpeg/libavcodec/avcodec.h @@ -28,6 +28,7 @@ */ #include +#include #include "libavutil/samplefmt.h" #include "libavutil/attributes.h" #include "libavutil/avutil.h" @@ -4181,4 +4182,9 @@ AVCPBProperties *av_cpb_properties_alloc(size_t *size); * @} */ +typedef struct AscendEncPrivateData { + bool next_is_I_frame; + bool is_instant; +} AscendEncPrivateData_t; + #endif /* AVCODEC_AVCODEC_H */