我想用 C 语言制作一个快速程序,它将打开一个视频,将每一帧保存为 ppm,并转储运动矢量。我能找到的所有教程都来自大约十年前,并调用了已弃用或不存在的函数。
是否有任何好的在线资源、网站、视频或教科书涵盖了现代方法来做这些类型的事情?
我使用 ffmpeg 和 libav 已经有好几年了,也没有找到像样的最近的 API 级教程。有时我只需要深入研究源代码就可以弄清楚发生了什么以及如何使用它。此外,阅读 CLI 程序(使用底层库)的源代码也可以提供丰富的信息。
事实上,ffmpeg建议您只阅读源代码,无论是真正的 OSS 项目还是它们的示例。Libav提供 Doxygen 文档,但没有分步教程。IMO 这是典型的开源项目:源代码可能很好,但通常缺乏文档。
退后一步,您可能会考虑使用OpenCV,它有很好的文档记录(存在书籍!)并且具有更简单的 API。最后,ppm 是一种很简单的格式,你可以自己写一个 50 行的函数来保存像素,我在这里找到了一个运动提取程序。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <math.h>
#define __STDC_CONSTANT_MACROS
//FFMPEG LIBRARIES
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavutil/common.h"
#include "libavutil/channel_layout.h"
#include "libavutil/imgutils.h"
#include "libavutil/mathematics.h"
#include "libavutil/samplefmt.h"
#include "libavutil/time.h"
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavfilter/avfilter.h"
#include "libavdevice/avdevice.h"
#include "libavfilter/avfiltergraph.h"
#include "libavfilter/buffersink.h"
}
using namespace std;
void SaveMyFrame(AVFrame *sAVFrame , int swidth, int sheight, int iFrame)
{
FILE *pfile;
char szFilename[32];
int y;
sprintf(szFilename , "frame%d.ppm" , iFrame);
pfile = fopen(szFilename , "wb");
if(pfile == NULL)
{
cout<<"\n\ncould'nt open file";
return;
}
fprintf(pfile , "P6\n%d %d\n255\n" , swidth , sheight );
for( y=0; y<sheight; y++)
{
fwrite(sAVFrame->data[0]+y*sAVFrame->linesize[0] , 1 , swidth*3 , pfile );
}
fclose(pfile);
}
int CaptureScene(int VideoStreamIndx ,
AVFormatContext *bAVFormatContext ,
AVCodecContext *bAVCodecContext,
AVCodec *bAVCodec )
{
AVPacket bAVPacket;
AVFrame *bAVFrame = NULL;
bAVFrame = av_frame_alloc();
AVFrame *bAVFrameRGB = NULL;
bAVFrameRGB = av_frame_alloc();
if(bAVFrame == NULL)
{
cout<<"\n\nframe alloc failed";
}
if(bAVFrameRGB == NULL)
{
cout<<"\n\nframe alloc RGB failed";
}
int numBytes;
uint8_t *buffer = NULL;
numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24 , bAVCodecContext->width,bAVCodecContext->height, 32); // avpicture_get_size deprecated
buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
avpicture_fill((AVPicture *)bAVFrameRGB , buffer , AV_PIX_FMT_RGB24 , bAVCodecContext->width , bAVCodecContext->height);
int framefinish;
struct SwsContext *sws_ctx = NULL;
sws_ctx = sws_getContext( bAVCodecContext->width,
bAVCodecContext->height,
bAVCodecContext->pix_fmt,
bAVCodecContext->width,
bAVCodecContext->height,
AV_PIX_FMT_RGB24,
SWS_BILINEAR,
NULL,NULL,NULL);
int i =0;
while(av_read_frame(bAVFormatContext,&bAVPacket) >=0)
{
if(bAVPacket.stream_index == VideoStreamIndx)
{
avcodec_decode_video2(bAVCodecContext , bAVFrame , &framefinish , &bAVPacket);
if(framefinish)
{
// convert image from native format to RGB
sws_scale(sws_ctx , (uint8_t const* const *)bAVFrame->data ,
bAVFrame->linesize , 0, bAVCodecContext->height,
bAVFrameRGB->data , bAVFrameRGB->linesize);
// save frame to disk
if(++i <= 100)SaveMyFrame(bAVFrameRGB , bAVCodecContext->width , bAVCodecContext->height , i );
}
}
}
av_free(bAVFrame);
av_free(bAVFrameRGB);
}
int main()
{
avdevice_register_all();
avcodec_register_all();
av_register_all();
char *dev_name = "/dev/video0";
int VideoStreamIndx = -1;
AVCodecContext *pAVCodecContext = NULL;
AVCodec *pAVCodec = NULL;
AVInputFormat *inputFormat =av_find_input_format("v4l2");
AVDictionary *options = NULL;
av_dict_set(&options, "framerate", "20", 0);
AVFormatContext *pAVFormatContext = NULL;
if(avformat_open_input(&pAVFormatContext, dev_name, inputFormat, NULL) != 0)
{
cout<<"\nError : could'nt open video source\n\n";
return -1;
}
if( avformat_find_stream_info( pAVFormatContext , NULL) < 0)
{
cout<<"Error : streams not found";
return -1;
}
av_dump_format(pAVFormatContext , 0 , "/dev/video1" , 0 );
for(int i=0; i<pAVFormatContext->nb_streams ;i++ )
{
if( pAVFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) // if video stream found then get the index.
{
VideoStreamIndx = i;
break;
}
}
if((VideoStreamIndx) == -1)
{
cout<<"Error : video streams not found";
return -1;
}
pAVCodecContext = pAVFormatContext->streams[VideoStreamIndx]->codec;
pAVCodec = avcodec_find_decoder( pAVCodecContext->codec_id );
if(pAVCodec == NULL)
{
fprintf(stderr,"Unsupported codec !");
return -1;
}
int value = avcodec_open2(pAVCodecContext , pAVCodec , NULL);
if( value < 0)
{
cout<<"Error : Could not open codec";
return -1;
}
int Vwidth , Vheight , videoFPS , videoBaseTime , duration_2 ;
int sframe , length , Fheight;
/*
To fetch/display some media information programatically
*/
//int64_t duration_1 = pAVFormatContext->duration;
videoFPS = av_q2d(pAVFormatContext->streams[VideoStreamIndx]->r_frame_rate);
videoBaseTime = av_q2d(pAVFormatContext->streams[VideoStreamIndx]->time_base);
Vwidth = pAVFormatContext->streams[VideoStreamIndx]->codec->width;
Vheight = pAVFormatContext->streams[VideoStreamIndx]->codec->height;
//duration_2 = (unsigned long)pAVFormatContext->streams[VideoStreamIndx]->duration*(videoFPS*videoBaseTime);
cout<<"Video FPS :"<<videoFPS;
cout<<"\n\n width : "<<Vwidth;
cout<<"\n\n height : "<<Vheight;
cout<<"\n\n time base"<<videoBaseTime;
//cout<<"\n\nduration (1): "<<duration_1;
//cout<<"\n\nduration (2): "<<duration_2;
CaptureScene( VideoStreamIndx , pAVFormatContext , pAVCodecContext , pAVCodec );
avcodec_close(pAVCodecContext);
avformat_close_input(&pAVFormatContext);
return 0;
}
此 C++ 代码将帮助您从基于 USB (UVC) 的相机中获取 .ppm 图像。它以 .ppm 格式保存。linux 使用 v4l2,windows 使用 dshow。代替相机帧馈送,视频也可以用作输入。此代码使用最新的 ffmpeg 库编写。
libavutil 55. 24.100 / 55. 24.100
libavcodec 57. 43.100 / 57. 43.100
libavformat 57. 37.100 / 57. 37.100
libav 设备 57. 0.101 / 57. 0.101
libavfilter 6. 46.100 / 6. 46.100
libswscale 4. 1.100 / 4. 1.100
libswresample 2. 0.101 / 2. 0.101
libpostproc 54. 0.100 / 54. 0.100