firefly rk3399使用ffmpeg硬解码

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

注意:这篇文章仅针对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_rkmpphevc_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一帧,绝大多数场景够用了。

相关推荐
「QT(C++)开发工程师」4 小时前
【FFmpeg应用场景概述】
ffmpeg
cuijiecheng20184 小时前
音视频入门基础:AAC专题(6)——FFmpeg源码中解码ADTS格式的AAC的Header的实现
ffmpeg·音视频·aac
yunhuibin4 小时前
ffmpeg面向对象——参数配置秘密探索及其设计模式
学习·设计模式·ffmpeg
superconvert1 天前
主流流媒体的综合性能大 PK ( smart_rtmpd, srs, zlm, nginx rtmp )
websocket·ffmpeg·webrtc·hevc·rtmp·h264·hls·dash·rtsp·srt·flv
cuijiecheng20181 天前
音视频入门基础:AAC专题(8)——FFmpeg源码中计算AAC裸流AVStream的time_base的实现
ffmpeg·音视频·aac
0点51 胜1 天前
[ffmpeg] 视频格式转换
ffmpeg
Jerry 二河小鱼2 天前
在Linux中安装FFmpeg
linux·运维·服务器·ffmpeg
0点51 胜3 天前
[ffmpeg] 音视频编码
ffmpeg·音视频
0点51 胜3 天前
[ffmpeg]音频格式转换
开发语言·c++·ffmpeg
PlumCarefree3 天前
基于鸿蒙API10的RTSP播放器(五:拖动底部视频滑轨实现跳转)
华为·ffmpeg·音视频