ffmpeg 取摄像头数据(二)

通过ffmpeg取摄像头原始数据

windows下打开的设备名“HD webcam”,通过设备管理器查看;linux下一般插入USB设备后,会在生成设备节点/dev/video0,如果没有的话通过lsusb查看,可以看到有挂载信息(usb摄像头驱动vid、pid),此时可能是uvc驱动的问题,有的裁剪版操作系统就会出现这种,比如openwrt。

准备工作:
USB摄像头:“HD webcam”,输入格式MJPEG。
解码器:AV_CODEC_ID_MJPEG
解码后的格式:AV_PIX_FMT_NV12 (可以用yuv420p等,只是我的intel集显带有的h264_qsv硬编码器支持NV12,所以使用这种格式)
环境:windows/linux

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
#include <libavutil/time.h>
#include <libswresample/swresample.h>
#include <libavformat/avformat.h>
#include <libavutil/mathematics.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>

void showDshowDeviceOption(char *devName){
    AVFormatContext *pFormatCtx = avformat_alloc_context();
    AVDictionary* options = NULL;
    av_dict_set(&options,"list_options","true",0);
    AVInputFormat *iformat = av_find_input_format("dshow");
    avformat_open_input(&pFormatCtx,devName,iformat,&options);
    avformat_free_context(pFormatCtx);
}

int main(){
    int ret;
    AVCodecContext  *pCodecCtx;  //解码器上下文
    AVCodec         *pCodec; //解码器
    AVFormatContext *ifmtCtx;//摄像头输入上下文
    AVDictionary *opt = NULL;//配置参数
    //初始化开始
    avdevice_register_all();
    av_register_all();//这个函数在最新版已经弃用,在windows下可以注释掉,但是在linux下会有问题
    ifmtCtx= avformat_alloc_context();
    AVInputFormat *ifmt=av_find_input_format("dshow"); //windows下dshow
    //AVInputFormat *ifmt=av_find_input_format("video4linux2");  //linux
    if(ifmt==NULL)
    {
        printf("输入格式错误.\n");
        return -1;
    }
    av_dict_set_int(&opt, "rtbufsize",  2*1024*1024, 0);  //为摄像头图像采集分配的内存,太小会丢失,太大会导致延时。可以设置为128*1024*1024
    av_dict_set(&opt,"start_time_realtime",0,0); //直播减少延时的,但是测试没啥效果
    av_dict_set(&opt,"video_size","1280x720",0); //设置分辨率,要看是否支持。windows可通过showDshowDeviceOption("dshow")查看。
    av_dict_set(&opt,"framerate","30",0);

    if(avformat_open_input(&ifmtCtx,"video=HD Camera",ifmt,&opt)!=0) //
    {
        printf("无法打开输入流.\n");
        return -2;
    }
    if(avformat_find_stream_info(ifmtCtx,NULL)<0)
    {
        printf("找不到流信息.\n");
        return -3;
    }

    pCodecCtx = avcodec_alloc_context3(NULL);
    if (pCodecCtx == NULL)
    {
        printf("Could not allocate AVCodecContext\n");
        return -4;
    }
    avcodec_parameters_to_context(pCodecCtx, ifmtCtx->streams[0]->codecpar);//由于一些结构体弃用(AVCodecContext *codec;),选择使用codecpar,当前只有视频流,因此streams[0]就为视频流,省略遍历过程。
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id); //这里就会得到MJPEG解码器
    if(pCodec==NULL)
    {
        printf("找不到编解码器。\n");
        return -5;
    }
    if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)
    {
        printf("无法打开编解码器。\n");
        return -6;
    }

    //初始化工作已经完成,接下来取原始图像并解码。
    AVPacket *inPacket = av_packet_alloc(); //摄像头取到的数据,待解码,先分配空间。
    AVFrame *pFrame = av_frame_alloc(); //分配空间存储解码后的信息
    while(1){
        if(av_read_frame(ifmtCtx, inPacket)>=0)//取摄像头数据,以阻塞的形式,帧率为30的话大概33ms返回一次。
        {
            if(inPacket->stream_index==0)
            {
                ret = avcodec_send_packet(pCodecCtx, inPacket);
                if (ret < 0) {
                    fprintf(stderr, "Error sending a packet for decoding\n");
                    return -7;
                }
                while (ret >= 0) {
                    ret = avcodec_receive_frame(pCodecCtx, pFrame);
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                        break;
                    else if (ret < 0) {
                        fprintf(stderr, "Error during decoding\n");
                        return -8;
                    }
                    printf("format:%d width:%d height:%d size:%d\n",pFrame->format,pFrame->width,pFrame->height,pFrame->pkt_size);
                 /*pFrame就是解码后的数据,此时可以存入ringbuf,另外一个线程做h264编码
                    此时对解码后的数据做处理,比如加水印、格式转换(如:YUV420p转NV12)*/
                }
                av_packet_unref(inPacket);//必须要解引用,否则会内存泄露
            }
        }
    }
    //当前为不退出程序,故不对分配的内存进行释放。
    return 0;
}

编译完成,执行程序:
ffmpeg 取摄像头数据(二)
说明:
1.format:13,是默认的解码后的格式,也就是nv12
2.width、height:像素宽高
3.size:解码后每一帧的数据大小
4.具体数据保存在AVFrame 的uint8_t *data[AV_NUM_DATA_POINTERS];字段中,与yuv结构有关系

接下来会对解码后的图像做水印处理并硬编码为h264.

上一篇:FFmpeg libswscale源码分析3-scale滤镜源码分析


下一篇:libavutil/error.h:132:58: error: taking address of temporary array av_make_error_string..