zoukankan      html  css  js  c++  java
  • JavaCV FFmpeg采集摄像头YUV数据

    前阵子使用利用树莓派搭建了一个视频监控平台( 传送门 ),不过使用的是JavaCV封装好的 OpenCVFrameGrabber FFmpegFrameRecorder 。 
    其实在 javacpp 项目集中有提供FFmpeg的JNI封装,可以直接使用FFmpeg API的来处理音视频数据,下面是一个简单的案例,通过FFmpeg API采集摄像头的YUV数据。

    javacpp-ffmpeg依赖:

                             <dependency>
        <groupId>org.bytedeco.javacpp-presets</groupId>
        <artifactId>ffmpeg</artifactId>
        <version>${ffmpeg.version}</version>
    </dependency>  
    1. 查找摄像头设备

    要采集摄像头的YUV数据,首先得知道摄像头的设备名称,可以通过FFmpeg来查找摄像头设备。

                             ffmpeg.exe -list_devices true -f dshow -i dummy  

    在我的电脑上结果显示如下: 

    其中 “Integrated Camera” 就是摄像头的设备名称。

    2. 利用FFmpeg解码

    采集摄像头数据即将摄像头作为视频流输入,通过FFmpeg解码获取视频帧,然后将视频帧转为YUV格式,最后将数据写入文件即可。 
    下面是FFmpeg解码的流程:

    3. 开发视频帧采集器

    根据FFmpeg的解码流程,实现视频帧采集器大概需要经过以下几个步骤:

    FFmpeg初始化

    首先需要使用 av_register_all() 这个函数完成编码器和解码器的初始化,只有初始化了编码器和解码器才能正常使用;另外要采集的是设备,所以还需要调用 avdevice_register_all() 完成初始化。

    分配AVFormatContext

    接着需要分配一个AVFormatContext,可以通过 avformat_alloc_context() 来分配AVFormatContext。

                             pFormatCtx = avformat_alloc_context();  

    打开视频流

    通过 avformat_open_input() 来打开视频流,这里需要注意的是input format要指定为 dshow ,可以通过 av_find_input_format("dshow")获取AVInputFormat对象。

                             ret = avformat_open_input(pFormatCtx, String.format("video=%s", input), av_find_input_format("dshow"), (AVDictionary) null);  

    查找视频流

    需要注意的是,查找视频流之前需要调用 avformat_find_stream_info() ,下面是查找视频流的代码:

                             ret = avformat_find_stream_info(pFormatCtx, (AVDictionary) null);
    for (int i = 0; i < pFormatCtx.nb_streams(); i++) {
        if (pFormatCtx.streams(i).codec().codec_type() == AVMEDIA_TYPE_VIDEO) {
            videoIdx = i;
            break;
        }
    }  

    打开解码器

    可以通过视频流来查找解码器,然后打开解码器,对视频流进行解码,Java代码如下:

                             pCodecCtx = pFormatCtx.streams(videoIdx).codec();
    pCodec = avcodec_find_decoder(pCodecCtx.codec_id());
    if (pCodec == null) {
        throw new FFmpegException("没有找到合适的解码器:" + pCodecCtx.codec_id());
    }
    // 打开解码器
    ret = avcodec_open2(pCodecCtx, pCodec, (AVDictionary) null);
    if (ret != 0) {
        throw new FFmpegException(ret, "avcodec_open2 解码器打开失败");
    }  

    采集视频帧

    最后就是采集视频帧了,这里需要注意的是采集摄像头的视频流解码得到的不一定是YUV格式的视频帧,所以需要对视频帧进行转化一下(videoConverter.scale(pFrame))。

                             public AVFrame grab() throws FFmpegException {
        if (av_read_frame(pFormatCtx, pkt) >= 0 && pkt.stream_index() == videoIdx) {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, got, pkt);
            if (ret < 0) {
                throw new FFmpegException(ret, "avcodec_decode_video2 解码失败");
            }
            if (got[0] != 0) {
                return videoConverter.scale(pFrame);
            }
            av_packet_unref(pkt);
        }
        return null;
    }  
    4. 将视频帧数据写入文件

    通过视频解码之后可以得到YUV格式的视频帧,只需要将视频帧的数据写入文件就可以完成整个摄像头YUV数据的采集流程,RGB数据是存在AVFrame.data[0]中,而YUV格式的数据分三个地方存储,Y数据存在AVFrame.data[0],U数据存在AVFrame.data[1],V数据存在AVFrame.data[2],其中U、V的数量是Y的1/4。 
    所以只需要根据YUV存储的位置和容量取出数据即可:

                             int fps = 25;
    Yuv420PGrabber g = new Yuv420PGrabber();
    g.open("Integrated Camera");
    		
    byte[] y = new byte[g.getVideoWidth() * g.getVideoHeight()];
    byte[] u = new byte[g.getVideoWidth() * g.getVideoHeight() / 4];
    byte[] v = new byte[g.getVideoWidth() * g.getVideoHeight() / 4];
    //  1280x720
    OutputStream fos = new FileOutputStream("yuv420p.yuv");
    for (int i = 0; i < 200; i ++) {
        AVFrame avFrame = g.grab();
        avFrame.data(0).get(y);
        avFrame.data(1).get(u);
        avFrame.data(2).get(v);
        fos.write(y);
        fos.write(u);
        fos.write(v);
        Thread.sleep(1000 / fps);
    }
    fos.flush();
    fos.close();
    		
    g.close();  
    5. 播放采集的YUV数据

    采集的YUV数据可以通过YUV Player Deluxe,效果如下:

    也可以通过ffplay来播放,命令如下

                             ffplay.exe -f rawvideo -video_size 1280x720 yuv420p.yuv  

    效果如下:

    ========================================================= 
    视频帧采集器源码可 关注 公众号 “HiIT青年” 发送 “ffmpeg-yuv” 获取。

    HiIT青年 
    关注公众号 ,阅读更多文章。

  • 相关阅读:
    linux-命令
    linux-命令-cd,ls
    linux-命令-概述
    linux-文件
    IOPS和带宽
    随记
    CAP,Base原理
    DateUtil
    JDK SPI
    001-进程 和 线程
  • 原文地址:https://www.cnblogs.com/huilixieqi/p/13793162.html
Copyright © 2011-2022 走看看