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
相关推荐
天天摸鱼的java工程师22 分钟前
QPS 10 万,任务接口耗时 100ms,线程池如何优化?
java·后端·面试
双向3322 分钟前
从O(n²)到O(n log n):深度剖析快速排序的内存优化与cache-friendly实现
后端
回家路上绕了弯24 分钟前
深度解析:频繁 Full GC 的诊断与根治方案
jvm·后端
武子康26 分钟前
大数据-57 Kafka 高级特性 Producer 消息发送流程与核心配置详解
大数据·后端·kafka
知其然亦知其所以然27 分钟前
MySQL社招面试题:索引有哪几种类型?我讲给你听的不只是答案!
后端·mysql·面试
天天摸鱼的java工程师30 分钟前
掘金图片上传被拒:一次由CheckAuthenticationError引发的密钥‘失踪’迷案
java·后端
福大大架构师每日一题31 分钟前
2025-08-01:粉刷房子Ⅳ。用go语言,给定一个偶数个房屋排列在一条直线上,和一个大小为 n x 3 的二维数组 cost,其中 cost[i][j] 表
后端
error_cn32 分钟前
网络i_o对cpu负载分析
后端
bug菌33 分钟前
学生信息管理系统,真的是码农的必修课吗?
java·后端·java ee
就是帅我不改34 分钟前
深入实战建造者模式:在订单系统中的应用
后端·架构