[FFMPEG]-採集Ubuntu桌面&攝像頭

命令行實現

採集攝像頭

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

API實現

採集桌面&攝像頭的畫面,並保存爲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)

參考文章

FFMPEG(一) 從V4L2捕獲攝像頭數據
FFmpeg接口-AVDictionary的使用介紹和源碼分析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章