提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
注意:这篇文章仅针对firefly设备,我在rk3399上操作成功,理论上也适合rk356x或358x系列的,我手上没有这些设备,所以没法测试。
今天这个方法比较简单,使用起来也有些限制,但是对于很多人来说够用了。
Device:firefly rk3399
OS:Ubuntu-20.04,Ubuntu-18.04
一、准备一个firefly的rk3399
为什么一定要是firefly的呢?首先我手上只有firefly的设备,其次这个方法要使用firefly的软件源。
二、使用步骤
1.检查ffmpeg版本
没错,这个方法比直接使用mpp简单,它将mpp和ffmpeg结合了起来,操作也大大简化。前提是ffmpeg的版本必须是firefly编译的版本。
bash
ffmpeg
ffmpeg version 4.2.4-1ubuntu1.0firefly3 Copyright (c) 2000-2020 the FFmpeg developers
ffmpeg版本带firefly
字样就是对的,如果你删除了firefly的源或者重新装过ffmpeg
就不行。如果你属于前者就跳过下一步,如果你属于后者不妨往下看:
1.查看源
bash
ls /etc/apt/source.list.d/
cat /etc/apt/source.list
如果是Ubuntu-18.04那么源是作为文件存放在/etc/apt/source.list.d/
的,如果是Ubuntu-20.04源是作为条目放在/etc/apt/source.list
文件里面的。
bash
deb http://wiki.t-firefly.com/firefly-ubuntu-repo focal main
如果你没有firefly的源,或者删掉了就重新加上就行了,切记备份数据可能覆盖!
2.查看解码器
确认ffmpeg支持我们需要的解码器:h264_rkmpp
和hevc_rkmpp
。
bash
ffmpeg -decoders | grep rkmpp
V..... h264_rkmpp h264 (rkmpp) (codec h264)
V..... hevc_rkmpp hevc (rkmpp) (codec hevc)
V..... vp8_rkmpp vp8 (rkmpp) (codec vp8)
V..... vp9_rkmpp vp9 (rkmpp) (codec vp9)
查看下是否有我们需要的条目,正常情况下应该是上面的4种。
注意:只有支持上面的4种你才能硬解码,否则不支持这种方式!
3.解码代码
前提是上面的你处理好了,接下来就开始写解码代码了。
main.cpp
cpp
#include <iostream>
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#ifdef __cplusplus
};
#endif
using namespace std;
int main(){
AVFormatContext *formatContext = avformat_alloc_context();
AVCodecContext *codecContext = nullptr;
AVCodec *codec = nullptr;
AVFrame *mpp_frame = av_frame_alloc();
AVPacket *packet = av_packet_alloc();
string hw_codec_name;//区分h264_rkmpp和hevc_rkmpp的
int videoStreamIndex = -1;
//替换本地视频,rtsp原理差不多,唯一的差别是需要初始化网络模块并设备一些网络参宿,这个网上随便能找到
avformat_open_input(&formatContext, "../1920x1080.mp4", nullptr, nullptr);
avformat_find_stream_info(formatContext, nullptr);
//找出视频的video index,音频也一样
//确定视频编码类型h264或hevc等等,vp8和vp9同理
for (int i = 0; i < formatContext->nb_streams; i++) {
if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStreamIndex = i;
if (formatContext->streams[i]->codecpar->codec_id == AV_CODEC_ID_H264) {
printf("video stream is H264\n");
hw_codec_name.assign("h264_rkmpp");
} else if (formatContext->streams[i]->codecpar->codec_id == AV_CODEC_ID_HEVC) {
printf("video stream is HEVC\n");
hw_codec_name.assign("hevc_rkmpp");
} else {
printf("video stream is Unknown!\n");
exit(1);
}
break;
}
}
//找到匹配的解码器,h264对应h264_rkmpp,hevc对应hevc_rkmpp,vp8和vp9同理
codec = avcodec_find_decoder_by_name(hw_codec_name.c_str());
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
//创建解码器上下文
codecContext = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(codecContext, formatContext->streams[videoStreamIndex]->codecpar);
avcodec_open2(codecContext, codec, nullptr);
printf("Stream video width is %d height is %d\n", codecContext->width, codecContext->height);
while (av_read_frame(formatContext, packet) >= 0) {
if (packet->stream_index == videoStreamIndex) {
int response = avcodec_send_packet(codecContext, packet);
if (response >= 0) {
//一个packet可能包含几帧,这里需要循环取,直到取完为止,一个frame就是一张图片
while (avcodec_receive_frame(codecContext, mpp_frame) == 0) {
//YUV格式是YUV420P,内存中排列方式是先Y再U再V,这里不细说了查下百科就知道了。
printf("y: %d, u: %d, v: %d\n", mpp_frame->linesize[0], mpp_frame->linesize[1],
mpp_frame->linesize[2]);
}
}
}
av_packet_unref(packet);
}
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
av_frame_free(&mpp_frame);
av_packet_free(&packet);
return 0;
}
CmakeLists.txt
bash
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(MPP)
set(CMAKE_CXX_STANDARD 11)
find_package(Threads REQUIRED)
add_executable(MPP
main.cpp
)
target_link_libraries(MPP Threads::Threads)
target_link_libraries(MPP rockchip_mpp)
target_link_libraries(MPP avcodec avformat avutil)
编译运行就可以了。
总结
1、实测1080p解码是5-6ms一帧,绝大多数场景够用了。