V1

2022/08/28阅读:15主题:橙心

linux下 使用FFmpeg 采集摄像头YUV数据

采集摄像头数据

从一个多媒体文件提取YUV数据

a)FFmpeg命令行

要得到原始YUV数据,需要解码操作,也就是编码的反向操作。

ffmpeg -i ./test.mp4 -an -c:v rawvideo -pix_fmt yuv420p out.yuv

-i:要提取的多媒体文件

-an:audio not 过滤掉音频,只要视频数据

-c:v:选择解码器

-pix_fmt:输出YUV数据格式

比对文件大小,观察下解码后的原始数据大小。

多媒体文件大小是1.8M,解码得到的YUV原始数据是538M,为什么会相差那么多?因为得到MP4多媒体文件,引用了H264压缩技术。可以想象到H264编码能力十分强。关于H264知识,我下期会介绍。

b)ffplay播放YUV文件。

在播放YUV文件时,我们要知道这个视频的分辨率以及YUV格式。

查看多媒体文件的大小

ffprobe ./test.mp4

分辨率是1280x720,YUV格式是yuv420p

播放YUV

ffplay -pix_fmt yuv420p -s 1280x720 out.yuv

2、C语言实现从外部设备中获取到YUV数据

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

#include <libavutil/avutil.h>
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#define WIDTH 640
#define HEIGHT 480

/*

*/

static AVFormatContext* open_dev()
{
    int ret = 0;
    char errors[1024] = {0,};
    char *devicename = "/dev/video0"//0机子的摄像头
    //ctx
    AVFormatContext *fmt_ctx = NULL;
    AVDictionary *options = NULL;

    avdevice_register_all();

    //get format
    AVInputFormat *iformat = av_find_input_format("video4linux2");

    //option 视频需要参数
    char video_size[10];
    sprintf(video_size, "%dx%d", WIDTH, HEIGHT);
    av_dict_set(&options,"video_size",video_size,0);
    
    //av_dict_set(&options,"pixel_format","yuyv422",0);
    
    if((ret = avformat_open_input(&fmt_ctx,devicename,iformat,&options)) < 0)
    {
        av_strerror(ret,errors,1024);
        fprintf(stderr,"Failed to open video device ,[%d]%s\n",ret,errors);
        return NULL;
    }
    return fmt_ctx;
}
void rec_video(){
    int ret = 0;
    int flag=-1;
    char input_command[128];
    AVFormatContext *fmt_ctx = NULL;
    AVPacket pkt;//数据存放
    flag=fcntl(0,F_GETFL); //获取当前flag
    av_log_set_level(AV_LOG_DEBUG);

    char *out = "./out.yuv";

    FILE *outfile = fopen(out,"wb+");

    //打开设备
    fmt_ctx = open_dev();
    
    flag |=O_NONBLOCK; //设置新falg
    fcntl(0,F_SETFL,flag); //更新flag

    av_init_packet(&pkt);//数据初始化 干净的空间;
    while((ret = av_read_frame(fmt_ctx,&pkt)) == 0)
    {
        av_log(NULL,AV_LOG_DEBUG,
                    "packet size is %d(%p)\n",
                    pkt.size,pkt.data);

        fwrite(pkt.data,1,pkt.size,outfile);
        fflush(outfile);

        av_packet_unref(&pkt);

        if((ret=read(0,input_command,sizeof(input_command))) > 0)
        {
            if(strncmp(input_command, "over",2) == 0)
            {
                av_log(NULL,AV_LOG_DEBUG,"over\n");
                break;
            }
            else
            {
                av_log(NULL,AV_LOG_DEBUG,"请重新输入\n");
            }
            memset(input_command, 0sizeof(input_command));
        }
    }

    fclose(outfile);
    avformat_close_input(&fmt_ctx);

    av_log(NULL, AV_LOG_DEBUG,"finish\n");
}
int main(int argc, char** argv)
{
    rec_video();
    return 0;
}

gcc编译:

gcc av_video.c -I/usr/ffmpeg4.1/include -L/usr/ffmpeg4.1/lib -o av_video -lavutil -lavdevice -lavformat -lavcodec

a)执行av_video文件

此时摄像头录像,获取YUV数据,程序默认使用yuv420p格式采集,从提示可以看到,最后采用yuyv422格式采集yuv数据。因为摄像头输出的yuv数据格式是yuyv422格式,怎样修改程序默认采集YUV格式? 取消av_dict_set(&options,"pixel_format","yuyv422",0);注释,指定YUV数据输入格式。

b)文件播放

yuv420p格式播放

ffplay -pix_fmt yuv420p -s 640x480 ./out.yuv

因为文件格式是YUYV422,与FFplay制定的播放格式不一致,导致显示效果有差异。

yuyv422格式播放

ffplay -pix_fmt yuyv422 -s 640x480 ./out.yuv

此时显示就正常了啦。

想了解更多,可以关注 小昭debug 公众号,交个朋友,交流下。

分类:

人工智能

标签:

图像处理

作者介绍

V1