ffmpeg新版本很多接口发生了变化,因此在使用时需要确定自己的版本,在这里使用的版本为4.1.4。
在此特别感谢雷博提供的相关文章:FFmpeg_雷霄骅的博客-CSDN博客
#include <iostream>
#include <stdio.h>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include <libavutil/log.h>
#include <libavutil/timestamp.h>
}
#define ERROR_STR_SIZE 1024
int main() {
int ret = -1;
int err_code;
char errors[ERROR_STR_SIZE];
AVFormatContext *ifmt_ctx = NULL;
AVCodecContext *pCodecCtx;
const AVOutputFormat *ofmt = NULL;
AVStream *in_stream1 = NULL;
int vedio_stream_indes = 0;
// 文件最大时长,保证音频和视频数据长度一致
double max_duration = 0;
AVPacket *pkt;
int stream1 = 0, stream2 = 0;
const char* video = "E:\\jiao\\project\\162300.mp4";
/*//初始化网络,如果要解封装网络数据格式,则可调用该函数。
avformat_network_init();*/
//av_register_all();//高版本已弃用
//打开输入文件(可解析视频参数)
ifmt_ctx = avformat_alloc_context();
if ((err_code = avformat_open_input(&ifmt_ctx, video, 0, 0)) != 0) {
av_strerror(err_code, errors, ERROR_STR_SIZE);
return -1;
}
if (avformat_find_stream_info(ifmt_ctx, NULL) < 0) {
printf("Couldn't find stream information.\n");
return -1;
}
// 找到视频流下标
vedio_stream_indes = -1;
for (int i = 0; i < ifmt_ctx->nb_streams; i++) {
if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
vedio_stream_indes = i;
break;
}
}
if (vedio_stream_indes == -1) {
printf("Didn't find a video stream");
return -1;
}
// 获取文件中的视频流
in_stream1 = ifmt_ctx->streams[vedio_stream_indes];
int rate = in_stream1->r_frame_rate.num; //视频帧率
int w = in_stream1->codecpar->width; //视频图像宽
int h = in_stream1->codecpar->height; //视频图像高
if (in_stream1->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
printf("the input is video.\n");
}
//软解码,查找解码器
const AVCodec *pCodec = avcodec_find_decoder(in_stream1->codecpar->codec_id);
if (pCodec == NULL) {
printf("Codec not found.\n");
return -1;
}
//获取编解码器上下文
pCodecCtx = avcodec_alloc_context3(pCodec);
if (pCodecCtx == NULL)
{
printf("get av codec context failed.\n");
return -1;
}
//设置编解码器上下文参数
if (avcodec_parameters_to_context(pCodecCtx, in_stream1->codecpar) < 0) {
printf("set param failed!\n");
return -1;
}
//打开编解码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) != 0) {
printf("Could not open codec.\n");
return -1;
}
AVFrame *frame;
//frame 初始化
frame = av_frame_alloc();
pkt = av_packet_alloc();
int frames = 0;
while (av_read_frame(ifmt_ctx, pkt)>=0) {
if (pkt->stream_index == vedio_stream_indes) {
//解封装,将包发送到解码队列中
ret = avcodec_send_packet(pCodecCtx, pkt);
if (ret < 0) {
continue;
}
if (ret >= 0) {
ret = avcodec_receive_frame(pCodecCtx, frame);
int keyfrmae = frame->key_frame; //是否为I帧
printf("keyframe:%d\n", keyfrmae);
if (ret == 0) {
frames += 1;
//printf("decodec sucess.\n");
printf("frames:%d\n",frames);
} else {
printf("receive failed.\n");
}
}
}
av_packet_unref(pkt); //必须要有,否则每帧解码av_read_frame都会产生堆内存,造成内存泄漏
}
av_free(frame->data);
avcodec_free_context(&pCodecCtx);
avformat_free_context(ifmt_ctx);
av_packet_free(&pkt);
return 1;
}
视频解码方法在rtsp流解码同样适用。
AVDictionary *options = NULL;
av_dict_set(&options, "rtsp_transport", "tcp", 0);
avformat_open_input(&ifmt_ctx, rtsp, 0, &options)
使用tcp传输方式获取数据
ffmpeg解码出来的图像转为opencv可以使用的Mat类数据 (BGR格式)
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
SwsContext *swsContext = sws_getContext(w, h, pCodecCtx->pix_fmt, w, h, AV_PIX_FMT_BGR24,
SWS_BICUBIC, NULL, NULL, NULL);
AVFrame *pFrameBGR = NULL;
uint8_t *out_bufferBGR = NULL;
pFrameBGR = av_frame_alloc();
//给pFrameBGR帧加上分配的内存;
out_bufferBGR = new uint8_t[w*h*3];
av_image_fill_arrays(pFrameBGR->data, pFrameBGR->linesize,
out_bufferBGR, AV_PIX_FMT_BGR24, w, h, 1);
sws_scale(swsContext, frame->data, frame->linesize, 0,
pCodecCtx->height, pFrameBGR->data, pFrameBGR->linesize);
Mat img(frame->height, frame->width, CV_8UC3, out_bufferBGR);
imshow("show", img);
waitKey(1);
av_free(pFrameBGR->data);
delete [] out_bufferRGB; //一定要使用delete[],而不是delete
sws_freeContext(swsContext); //释放内存
官方文档:CompilationGuide/Ubuntu – FFmpeg
参考文档: FFmpeg 3.1.11版本源码编译
源码下载地址:Index of /releases (ffmpeg.org)
尝试了两天终于成功可以使用了,现在记录一下中间遇到的问题以及是如何解决的:
最终使用的版本为 ffmpeg4.1.4,链接:http://ffmpeg.org/releases/ffmpeg-4.1.4.tar.bz2
步骤:
##下载、解码
wget http://ffmpeg.org/releases/ffmpeg-4.1.4.tar.bz2
tar xvf ffmpeg-4.1.4.tar.bz2
##编译安装
cd ffmpeg-4.1.4.tar.bz2
mkdir build
cd build
../configure --prefix=安装路径 --enable-shared --enable-pthreads --enable-gpl --enable-libx264
make -j
make install
export LD_LIBRARY_PATH=安装路径:$LD_LIBRARY_PATH
##配置路径
cd /etc/ld.so.conf.d/
vim ffmpeg.conf ##配置ffmpeg lib路径
ldconfig
问题一:
invalid suffix on literal; C++11 requires a space between literal and string
解决方案:
问题二:
Invalid data found when processing input(c++代码运行avformat_open_input()抛出异常)
解决方案:
重新编译ffmpeg
问题三:
EOORR:libx264 not found(在arm环境中出现)
解决方案:
../configure --prefix=安装路径 --enable-shared 去掉后面的参数
g++ rtsp.cpp -fPIC -shared -o rtsp -I /opt/ffmpeg/include/ -L /opt/ffmpeg/lib -lavcodec -lavformat -lavutil -lswscale -lswresample -lavfilter
其中:/opt/ffmpeg/include/为头文件路径 ,/opt/ffmpeg/lib为库文件路径。 编译成功后直接运行./rtsp即可。
from ctypes import *
self.handle.getframe.restype = POINTER(c_ubyte*self.size)
pp = self.handle.getframe()
data = np.frombuffer(pp.contents.value,np.uint8).reshape([self.height,self.width,3])
主要难点在于uint8_t*数据的获取,转换后的指针数据通过np.frombuffer转换为numpy格式数据。