采集摄像头
ffmpeg -f video4linux2 -s 640x480 -pixel_format yuyv422 -i /dev/video1 out.mp4 -loglevel debug
采集桌面
ffmpeg 需要使能: --enable-libxcb
ffmpeg -f x11grab -framerate 25 -video_size 1280*720 -i :0.0 out.mp4
采集桌面(树莓派)
ffmpeg -f x11grab -s 1280x720 -i :0.0 -f alsa -i hw:0,0 -f flv out.mp4
采集桌面&摄像头的画面,并保存为YUV420P格式
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
using namespace std;
extern "C"
{
#include <libavutil/log.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}
#define YUV420P_DUMP
int main(int argc, char *argv[])
{
av_log_set_level(AV_LOG_INFO);
if(argc < 3|| strlen(argv[1]) == 0 || strlen(argv[2]) == 0 )
{
av_log(nullptr, AV_LOG_ERROR, "usage: ./FFrecorder format filename.\n");
av_log(nullptr, AV_LOG_ERROR, "for camera: ./FFrecorder video4linux2 /dev/video0.\n");
av_log(nullptr, AV_LOG_ERROR, "for screen: ./FFrecorder x11grab :0.0 .\n");
return -1;
}
avcodec_register_all();
avdevice_register_all();
AVInputFormat *pInputFmt;
AVDictionary *pOptions =nullptr;
AVFormatContext *pFmtCtx = nullptr;
AVCodecContext *pCodecCtx =nullptr;
AVCodec *pCodec =nullptr;
struct SwsContext *pSwsCtx=nullptr;
AVPacket *pPacket = nullptr;
AVFrame *pFrame = nullptr;
AVFrame *pYUVFrame = nullptr;
int vieoStreamIndex = -1;
int retValue = 0;
int bGotFrame = 0;
int frameCnt = 10;
pInputFmt = av_find_input_format(argv[1]);
if(pInputFmt == nullptr)
{
av_log(nullptr, AV_LOG_ERROR, "cant not find input format.\n");
return -2;
}
if(strcmp(argv[1],"video4linux2") == 0)
{
av_dict_set(&pOptions, "video_size", "1280*720", AV_DICT_MATCH_CASE);
av_dict_set(&pOptions, "framerate", "10", AV_DICT_MATCH_CASE);
}
else if (strcmp(argv[1],"x11grab") == 0)
{
av_dict_set(&pOptions, "video_size", "1280*720", AV_DICT_MATCH_CASE);
av_dict_set(&pOptions, "framerate", "10", AV_DICT_MATCH_CASE);
}
if (avformat_open_input ( &pFmtCtx, argv[2], pInputFmt , &pOptions) < 0){
av_log(nullptr, AV_LOG_ERROR, "cant not open input file.\n");
return -3;
}
/* print device information*/
av_dump_format(pFmtCtx, 0, argv[2], 0);
if(avformat_find_stream_info(pFmtCtx, nullptr)<0)
{
av_log(nullptr, AV_LOG_ERROR, "cant not find stream information.\n");
return -4;
}
vieoStreamIndex = -1;
for(int iter=0; iter < static_cast<int>(pFmtCtx->nb_streams); iter++)
{
if(pFmtCtx->streams[iter]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
{
vieoStreamIndex=iter;
break;
}
}
if(vieoStreamIndex == -1){
av_log(nullptr, AV_LOG_ERROR, "cant not find a video stream.\n");
return -5;
}
pCodecCtx=pFmtCtx->streams[vieoStreamIndex]->codec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==nullptr){
av_log(nullptr, AV_LOG_ERROR, "cant not find codec.\n");
return -6;
}
av_log(NULL, AV_LOG_INFO, "using codec:%s.\n", pCodec->name);
if(avcodec_open2(pCodecCtx, pCodec, nullptr)<0)
{
av_log(nullptr, AV_LOG_ERROR, "cant not open codec.\n");
return -7;
}
pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P,
SWS_BICUBIC, nullptr, nullptr, nullptr);
#ifdef YUV420P_DUMP
ofstream out_image("video.yuv", ios::binary);
if (!out_image.is_open())
{
av_log(nullptr, AV_LOG_ERROR, "Failed to save yuv video: %s\n", "video.yuv");
return -8;
}
#endif
pPacket = (AVPacket *)av_malloc(sizeof(AVPacket));
pFrame = av_frame_alloc();
pYUVFrame = av_frame_alloc();
uint8_t *pYUVFrameBuffer = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1));
av_image_fill_arrays(pYUVFrame->data, pYUVFrame->linesize, pYUVFrameBuffer,
AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);
pYUVFrame->format = AV_PIX_FMT_YUV420P;
while(av_read_frame(pFmtCtx, pPacket)>=0)
{
if(pPacket->stream_index == vieoStreamIndex)
{
retValue = avcodec_decode_video2(pCodecCtx, pFrame, &bGotFrame, pPacket);
if(retValue < 0){
av_log(nullptr, AV_LOG_ERROR, "decode a frame error.\n");
break;
}
if(bGotFrame)
{
av_log(nullptr, AV_LOG_INFO, "get a frame.\n");
sws_scale(pSwsCtx, static_cast<const uint8_t *const *>(pFrame->data), pFrame->linesize, 0,
pCodecCtx->height, pYUVFrame->data, pYUVFrame->linesize);
//av_log(nullptr, AV_LOG_INFO, "pFrame->format %d.\n", pFrame->format );
//av_log(nullptr, AV_LOG_INFO, "pYUVFrame->format %d.\n", pYUVFrame->format );
if (static_cast<AVPixelFormat>(pYUVFrame->format) == AV_PIX_FMT_YUV420P)
{
#ifdef YUV420P_DUMP
int y_size = pCodecCtx->width * pCodecCtx->height;
out_image.write((char *)pYUVFrame->data[0], y_size);
out_image.write((char *)pYUVFrame->data[1], y_size / 4);
out_image.write((char *)pYUVFrame->data[2], y_size / 4);
//av_log(nullptr, AV_LOG_INFO, "write a frame.\n");
#endif
}
frameCnt--;
}
if(frameCnt == 0)
{
break;
}
}
}
#ifdef YUV420P_DUMP
out_image.close();
#endif
sws_freeContext(pSwsCtx);
av_free(pYUVFrameBuffer);
av_free(pPacket);
av_free(pYUVFrame);
av_free(pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFmtCtx);
av_free(pFmtCtx);
return 0;
}
######################################
#
######################################
#source file
#源文件,自动找所有.c和.cpp文件,并将目标定义为同名.o文件
SOURCE := $(wildcard *.c) $(wildcard *.cpp)
OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))
#target you can change test to what you want
#目标文件名,输入任意你想要的执行文件名
TARGET := FFrecorder
#compile and lib parameter
#编译参数
CXX := g++
LIBS := -L /usr/local/lib -lavformat -lavutil -lavdevice -lavcodec -lswresample -lavfilter -lswscale -pthread
LDFLAGS :=
DEFINES :=
INCLUDE := -I.
CFLAGS := -g -Wall -O3 -std=c11 $(DEFINES) $(INCLUDE)
CXXFLAGS:= -g -Wall -O3 -std=c++11 $(DEFINES) $(INCLUDE)
#i think you should do anything here
#下面的基本上不需要做任何改动了
.PHONY : everything objs clean veryclean rebuild
everything : $(TARGET)
all : $(TARGET)
objs : $(OBJS)
rebuild: veryclean everything
clean :
rm -fr *.so
rm -fr *.o
veryclean : clean
rm -fr $(TARGET)
$(TARGET) : $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)