周六在家没什么事,就来公司加班。
最近正好在做音视频的项目,避免不了要和FFMpeg打些交道。之前没怎么接用过FFMpeg,之前就是简单的用FFmpeg拉了RTSP流,截个图 缩放什么的 更深入的功能没有做过。想借助这个时间好好学习下FFMpeg。本来想在windows下做的,需要下载visual studio .网上看了下 VS太大了 真的太大了 动不动10几个G 公司的网速下载 看了下 不到500K 放弃了 。
正好项目开发是在ubuntu下的 有VMware的ubuntu环境。 我自己都不知道什么时候装了QT。
QT本来就是跨平台的,网上搜了下 在Ubuntu下可以用QT+FFMpeg+SDL来做开发。
之前已经下了FFMpeg的源码了 ,只不过编译的是海思平台ARM版本的 需要重新编译成ubuntu版本。
编译脚本如下,默认配置
./configure --prefix=./install_ubunt \
--disable-x86asm
接着 make make install 没啥问题
接下来QT新建一个NON-QT的 plain C或者C++项目都可以 ,修改pro配置文件
如下图
写了个测试程序 编译出错 更准确的应该是连接 如下图 莫名其妙的一大堆
没咋搞过 百度,大概知道了原因
pro文件 添加
LIBS += -lz 没问题 继续编译
又来了连接错误 如下图
这次是找不到av开头的函数,继续百度 百度说要把libavformat.a放在最前面
果然 放在前面就没这个错误了 FFMpeg的代码顺利执行 没问题 。
接下来要把SDL也集成进来。官网直接下源码http://www.libsdl.org/download-2.0.php
下下来 配置下 make make install
有个比较吭的地方是 sdl的configure 不支持相对路径只能用绝对路径 没办法
看这么一长串的目录都闹心,对了 说下 我习惯建一个build.sh 把这个配置写进去,直接执行对应的脚本,这样不同平台起一个不用的名字,比如arm平台需要修改编译链,增加删除裁剪一些功能什么的这,这样比较方便。
./configure --prefix=/home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/SDL2-2.0.16/install_ubuntu
OK 继续,改pro文件 把头文件的路径,lib都加进工程里面,网上copy一个简单的测试环境
编译 又是报错 SDL_SetVideoMode API什么的找不到,继续百度,百度说是这个API过期了,得用新的API好继续改 改好了 ok编译 又是 一连串红色错误 :
继续百度
说是要LIBS += -ldl ok 修改pro文件 编译 一切ok 运行 又出错了
继续百度 发现是 虚拟机下AUDIO设备不正常 所以失败了,SDL_Init那里 去掉 AUDIO 的flag 终于运行正常了。 看效果图
完整的PRO配置如下:
TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt
SOURCES += main.cpp
INCLUDEPATH += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt/include
INCLUDEPATH += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/SDL2-2.0.16/install_ubuntu/include
LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt/lib/libavformat.a
LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt/lib/libavcodec.a
LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt/lib/libavdevice.a
LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt/lib/libavfilter.a
LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt/lib/libavutil.a
LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt/lib/libswresample.a
LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt/lib/libswscale.a
LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/SDL2-2.0.16/install_ubuntu/lib/libSDL2.a
LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/SDL2-2.0.16/install_ubuntu/lib/libSDL2main.a
LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/SDL2-2.0.16/install_ubuntu/lib/libSDL2_test.a
LIBS += -lpthread
LIBS += -lxcb -lm
LIBS += -lz
LIBS += -ldl
main.cpp如下:
#include <iostream>
using namespace std;
#include<stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#define RTSPSOURCE1 "rtsp://uer:gd123456@192.168.2.121:554/Streaming/Channels/101" //720P
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "SDL2/SDL.h"
}
void SaveFrame(AVFrame *frame,int width, int height ,int iFrame)
{
FILE *fp;
char filename[100]={0};
int y = 0;
sprintf(filename,"frame_%d.ppm",iFrame);
fp = fopen(filename,"wb");
if(fp == NULL)
{
cout<<"fp is null "<<endl;
return;
}
fprintf(fp,"P6\n%d %d\n255\n",width,height);
for(y=0;y<height;y++)
{
fwrite(frame->data[0]+y*frame->linesize[0],1,width*3,fp);
}
fclose(fp);
cout<<"fp is end "<<endl;
}
int main()
{
AVFormatContext *pFormatCtx = NULL;
AVDictionary *options = NULL;
AVPacket *packet = NULL;
AVCodecContext* avc_cxt = NULL;
AVFrame* frame = NULL;
AVFrame* frameRGB = NULL;
AVCodec* codec ;
struct SwsContext *sws_ctx = NULL;
int ret = 0,i = 0;
char *input_file = RTSPSOURCE1;
int videoindex = -1;
uint8_t *buffer = NULL;
int bytes = 0;
int gotFrame = 0;
int w_width = 1280;
int w_height = 720;
Uint32 pixformat;
SDL_Rect rect;
SDL_Window *win = NULL;
SDL_Renderer *renderer = NULL;
SDL_Texture *texture = NULL;
ret = SDL_Init(SDL_INIT_VIDEO);
if (ret) {
cout<<"SDL init error :"<<ret<<endl;
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not initialize SDL - %s\n", SDL_GetError());
return ret;
}
av_dict_set(&options, "buffer_size", "1024000", 0); //Buffer size
av_dict_set(&options, "rtsp_transport", "tcp", 0); //UDP or TCP
av_dict_set(&options, "stimeout", "5000000", 0); //Timeout value :us
av_dict_set(&options, "max_delay", "500000", 0); //Max delay time:us
pFormatCtx = avformat_alloc_context(); //Init handle
//Open file or stream URL
ret = avformat_open_input(&pFormatCtx, input_file, NULL, &options);
if(ret != 0)
{
cout<<"open file failed:"<<input_file<<endl;
}
//Get video info
if (avformat_find_stream_info(pFormatCtx, NULL)<0)
{
printf("Couldn't find stream information.\n");
goto EXIT;
}
//Check if has video info
for (i = 0; i<pFormatCtx->nb_streams; i++)
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoindex = i;
break;
}
if (videoindex == -1)
{
printf("Didn't find a video stream.\n");
goto EXIT;
}
av_dump_format(pFormatCtx,0,input_file,0);
avc_cxt = pFormatCtx->streams[videoindex]->codec;
codec = avcodec_find_decoder(avc_cxt->codec_id);
if(!codec){
av_log(NULL,AV_LOG_ERROR,"No decoder ");
goto EXIT;
}
ret = avcodec_open2(avc_cxt,codec,NULL);
if(ret < 0){
av_log(NULL,AV_LOG_ERROR,"Can not open decoder");
goto EXIT;
}
frame = av_frame_alloc();
frameRGB = av_frame_alloc();
if(frameRGB == NULL)
{
goto EXIT;
}
packet = (AVPacket *)av_malloc(sizeof(AVPacket)); //Malloc packet
bytes= avpicture_get_size(AV_PIX_FMT_RGB24,avc_cxt->width,avc_cxt->height);
buffer = (uint8_t*)av_malloc(bytes*sizeof(uint8_t));
avpicture_fill((AVPicture *)frameRGB,buffer,AV_PIX_FMT_RGB24,avc_cxt->width,avc_cxt->height);
frameRGB->width = avc_cxt->width;
frameRGB->height = avc_cxt->height;
sws_ctx = sws_getContext(avc_cxt->width,avc_cxt->height,avc_cxt->pix_fmt,
frameRGB->width,frameRGB->height,AV_PIX_FMT_RGB24,SWS_BICUBIC,NULL,NULL,NULL);
win = SDL_CreateWindow("Media Player",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
w_width, w_height,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!win) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create window by SDL");
goto EXIT;
}
renderer = SDL_CreateRenderer(win, -1, 0);
if (!renderer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Renderer by SDL");
goto EXIT;
}
pixformat = SDL_PIXELFORMAT_IYUV;//YUV\u683c\u5f0f
texture = SDL_CreateTexture(renderer,
pixformat,
SDL_TEXTUREACCESS_STREAMING,
w_width,
w_height);
while(1)
{
if (av_read_frame(pFormatCtx, packet) >= 0)
{
if (packet->stream_index == videoindex)
{
//cout<<"packet size is :"<<packet->size<<endl;
avcodec_decode_video2(avc_cxt,frame,&gotFrame,packet);
if(gotFrame)
{
if(i++ % 25 == 0)
{
ret = sws_scale(sws_ctx, (uint8_t const * const *)frame->data,
frame->linesize, 0, frame->height,
frameRGB->data, frameRGB->linesize);
cout<<"ret :"<<ret<<endl;
SaveFrame(frameRGB,avc_cxt->width,avc_cxt->height,i);
if(i>200)
{
//goto EXIT;
}
}
SDL_UpdateYUVTexture(texture, NULL,
frame->data[0], frame->linesize[0],
frame->data[1], frame->linesize[1],
frame->data[2], frame->linesize[2]);
// Set Size of Window
rect.x = 0;
rect.y = 0;
rect.w = frameRGB->width;
rect.h = frameRGB->height;
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, &rect);
SDL_RenderPresent(renderer);
}
else
{
cout<<"avcodec_decode_video2 failed size is:"<<packet->size<<endl;
}
av_frame_unref(frame);
}
av_packet_unref(packet);
SDL_Event event;
SDL_PollEvent(&event);
switch (event.type) {
case SDL_QUIT:
goto EXIT;
default:
break;
}
}
}
EXIT:
av_free(buffer);
av_free(packet);
av_frame_free(&frameRGB);
av_frame_free(&frame);
avformat_close_input(&pFormatCtx);
if (win) {
SDL_DestroyWindow(win);
}
if (renderer) {
SDL_DestroyRenderer(renderer);
}
if (texture) {
SDL_DestroyTexture(texture);
}
SDL_Quit();
printf("%s This is a test \n",__FUNCTION__);
return 0;
}
SaveFrame是之前测试保存图片用的
前文提到SDL_Init里面有SDL_INIT_AUDIO 的时候回初始化失败 返回-1
网上搜了下 说要装两个东东 如下:
sudo apt-get install libasound2-dev libpulse-dev
下载完之后,SDL make clean 重新 build make make install 即可解决 返回-1 的问题。