demo01 ffmpeg 从usb uvc摄像头读取一张图片

v4l2-ctl 查看摄像头支持的图像格式

bash 复制代码
v4l2-ctl --list-formats-ext -d /dev/video60

从摄像头读取一张图片

main.c

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
#include <libavutil/imgutils.h>
#include <libavutil/pixfmt.h>
#include <libavcodec/avcodec.h>

void capture_one_frame(const char* device, const char* output_file, int width, int height, const char* pix_fmt) {
    AVFormatContext* fmt_ctx = NULL;
    const AVInputFormat* input_fmt = NULL;
    AVPacket pkt;
    AVFrame* frame = NULL;
    AVCodecParameters *codecpar = NULL;
    const AVCodec *codec = NULL;
    AVCodecContext *codec_ctx = NULL;
    FILE* fp = NULL;
    int ret;

    // 注册所有设备和格式
    avdevice_register_all();

    // 查找 V4L2 输入格式
    input_fmt = av_find_input_format("video4linux2");
    if (!input_fmt) {
        fprintf(stderr, "Cannot find input format\n");
        return;
    }

    // 打开设备
    ret = avformat_open_input(&fmt_ctx, device, input_fmt, NULL);
    if (ret < 0) {
        fprintf(stderr, "Cannot open input file\n");
        return;
    }

    // 获取流信息
    ret = avformat_find_stream_info(fmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Cannot find stream information\n");;
        goto end;
    }

    // 打印流信息
    printf("\n==== Stream Information ===\n\n");
    av_dump_format(fmt_ctx, 0, device, 0);
    printf("\n==== End of Stream Information ===\n");

    // 分配帧
    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Cannot allocate frame\n");
        goto end;
    }

    // 获取编解码器参数
    codecpar = fmt_ctx->streams[0]->codecpar;
    codec = avcodec_find_decoder(codecpar->codec_id);
    if (!codec) {
        fprintf(stderr, "Cannot find codec\n");
        goto end;
    }

    // 打印解码器名称
    printf("Decoder: %s\n", avcodec_get_name(codecpar->codec_id));

    // 分配编解码器上下文
    codec_ctx = avcodec_alloc_context3(codec);
    if (!codec_ctx) {
        fprintf(stderr, "Cannot allocate codec context\n");
        goto end;
    }

    // 将编解码器参数复制到编解码器上下文
    avcodec_parameters_to_context(codec_ctx, codecpar);

    // 打开编解码器
    ret = avcodec_open2(codec_ctx, codec, NULL);
    if (ret < 0) {
        fprintf(stderr, "Cannot open codec\n");
        goto end;
    }

    // 读取一帧
    ret = av_read_frame(fmt_ctx, &pkt);
    if (ret < 0) {
        fprintf(stderr, "Cannot read frame\n");
        goto end;
    }

    // 打印 AVPacket 的属性值
    printf("AVPacket properties:\n");
    printf("  Stream Index: %d\n", pkt.stream_index);
    printf("  PTS: %" PRId64 "\n", pkt.pts);
    printf("  DTS: %" PRId64 "\n", pkt.dts);
    printf("  Duration: %" PRId64 "\n", pkt.duration);
    printf("  Size: %d bytes\n", pkt.size);
    printf("  Flags: %d\n", pkt.flags);
    printf("  Data: %p\n", pkt.data);

    // 解码帧
    ret = avcodec_send_packet(codec_ctx, &pkt);
    if (ret < 0) {
        fprintf(stderr, "Cannot send packet\n");
        goto end;
    }

    ret = avcodec_receive_frame(codec_ctx, frame);
    if (ret < 0) {
        fprintf(stderr, "Cannot receive frame\n");
        goto end;
    }

    // 打印 AVFrame 的属性值
    printf("AVFrame properties:\n");
    printf("  PTS: %" PRId64 "\n", frame->pts);
    printf("  DTS: %" PRId64 "\n", frame->pkt_dts);
    printf("  Pict Type: %d\n", frame->pict_type);
    printf("  Format: %s\n", av_get_pix_fmt_name(frame->format));
    printf("  Width: %d\n", frame->width);
    printf("  Height: %d\n", frame->height);
    printf("  Linesize: %d\n", frame->linesize[0]);
    printf("  Data: %p\n", frame->data[0]);

    // 打开输出文件
    fp = fopen(output_file, "wb");
    if (!fp) {
        fprintf(stderr, "Cannot open output file\n");
        goto end;
    }

    // 写入帧数据
    fwrite(frame->data[0], 1, av_image_get_buffer_size(av_get_pix_fmt(pix_fmt), width, height, 1), fp);

end:
    // 清理
    avformat_close_input(&fmt_ctx);
    av_frame_free(&frame);
    avcodec_free_context(&codec_ctx);

    if (fp) {
        fclose(fp);
    }
}

int main(int argc, char* argv[]) {
    const char* device = "/dev/video60";
    const char* output_file = "output.yuv";
    int width = 1280;
    int height = 720;
    const char* pix_fmt = "yuyv422";

    capture_one_frame(device, output_file, width, height, pix_fmt);

    return 0;
}

Makefile

Makefile 复制代码
C_COMPILER	=	aarch64-rockchip1031-linux-gnu-gcc  
CFLAGS		=	--sysroot=/home/lingke/mnt \
				-I/home/lingke/mnt/usr/include/aarch64-linux-gnu
LDFLAGS		=	-B/home/lingke/mnt/usr/lib/aarch64-linux-gnu \
				-Wl,-rpath-link,/home/lingke/mnt/usr/lib/aarch64-linux-gnu \
				-lavcodec -lavformat -lavutil -lavfilter -lswscale -lswresample -lavdevice -lpostproc



main.exe: main.c
	$(C_COMPILER) $(CFLAGS) -o $@ $^ $(LDFLAGS)
	
clean:
	-rm -rf main.exe main.o

编译

go 复制代码
make

拷贝到开发板

css 复制代码
scp main.exe rk:~

在开发板上运行

bash 复制代码
./main.exe

生成 output.yuv,拷贝到主机,用ffplay查看

ffplay 查看 output.yuv

css 复制代码
ffplay -f rawvideo -pixel_format yuyv422 -video_size 1280x720 -i output.yuv
相关推荐
Mahir0834 分钟前
Spring 循环依赖深度解密:从问题本质到三级缓存源码级解析
java·后端·spring·缓存·面试·循环依赖·三级缓存
IT_陈寒5 小时前
Redis缓存击穿把我整不会了,原来还有这手操作
前端·人工智能·后端
kyriewen5 小时前
面试官让我查各部门工资最高的员工,我用AI三秒写出窗口函数,他愣了
后端·mysql·面试
文心快码BaiduComate5 小时前
干货|Comate Harness Engineering工程实践指南
前端·后端·程序员
光辉GuangHui5 小时前
Agent Skill 也需要测试:如何搭建 Skill 评估框架
前端·后端·llm
我是谁的程序员5 小时前
Mac 上生成 AppStoreInfo.plist 文件,App Store 上架
后端·ios
irving同学462386 小时前
Node 后端实战:JWT 认证与生产级错误处理
前端·后端
Master_Azur6 小时前
单元测试——Junit单元测试框架
后端
用户8356290780516 小时前
使用 Python 进行 Word 邮件合并
后端
用户8356290780516 小时前
Python 操作 PowerPoint OLE 对象
后端·python