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
相关推荐
幽络源小助理1 分钟前
SpringBoot+Vue车票管理系统源码下载 – 幽络源免费项目实战代码
vue.js·spring boot·后端
uzong30 分钟前
软件架构指南 Software Architecture Guide
后端
又是忙碌的一天30 分钟前
SpringBoot 创建及登录、拦截器
java·spring boot·后端
勇哥java实战分享1 小时前
短信平台 Pro 版本 ,比开源版本更强大
后端
学历真的很重要2 小时前
LangChain V1.0 Context Engineering(上下文工程)详细指南
人工智能·后端·学习·语言模型·面试·职场和发展·langchain
计算机毕设VX:Fegn08952 小时前
计算机毕业设计|基于springboot + vue二手家电管理系统(源码+数据库+文档)
vue.js·spring boot·后端·课程设计
上进小菜猪2 小时前
基于 YOLOv8 的智能杂草检测识别实战 [目标检测完整源码]
后端
韩师傅3 小时前
前端开发消亡史:AI也无法掩盖没有设计创造力的真相
前端·人工智能·后端
栈与堆3 小时前
LeetCode-1-两数之和
java·数据结构·后端·python·算法·leetcode·rust
superman超哥3 小时前
双端迭代器(DoubleEndedIterator):Rust双向遍历的优雅实现
开发语言·后端·rust·双端迭代器·rust双向遍历