javacpp-ffmpeg系列:

javacpp-FFmpeg系列之1:视频拉流解码成YUVJ420P,并保存为jpg图片

javacpp-FFmpeg系列之2:通用拉流解码器,支持视频拉流解码并转换为YUV、BGR24或RGB24等图像像素数据

javacpp-FFmpeg系列之3: 图像数据转换(BGR与BufferdImage互转,RGB与BufferdImage互转)

前言

本篇文章算是javacv系列的后续,javacv算是作者在ffmpeg基础上封装了一层,我们算是站在别人的肩膀上,尽管javacv还有很多不足的地方,这个暂且不谈。当然这次写的这篇算是回归底层实现了,用别人封装好的可能两三行就可以搞定的东西,这次偏偏想不开去参照了c++的实现,其中拉流部分参考了部分javacpp官方的demo(没有文档,等你来填233),解码和图片部分参考了c++的实现。

一、说明以及依赖

本篇文章主要是为了视频截图功能,本篇的代码可以复用到其他比如本地文件的截图,或者作为后台截图服务的实现库,也很稳定。

使用了javacpp1.4.1版本作为java调用c++的方式,ffmpeg使用了3.4.2版本

maven方式:

<properties>

<!-- javacpp当前版本 -->
        <javacpp.version>1.4.1</javacpp.version>
        <!-- ffmpeg版本 -->
        <ffmpeg.version>3.4.2</ffmpeg.version>

</properties>

<dependency>
                <groupId>org.bytedeco</groupId>
                <artifactId>javacv-platform</artifactId>
                <version>${javacpp.version}</version>
            </dependency>
            <dependency>
                <groupId>org.bytedeco</groupId>
                <artifactId>javacpp</artifactId>
                <version>${javacpp.version}</version>
            </dependency>
            <dependency>
                <groupId>org.bytedeco.javacpp-presets</groupId>
                <artifactId>ffmpeg-platform</artifactId>
                <version>${ffmpeg.version}-${javacpp.version}</version>
            </dependency>

二、实现功能

1、ffmpeg拉流(视频源可以是文件和流媒体(rtsp/rtmp/hls/flv等等))

2、将视频帧解码为yuvj420p图像数据

3、将yuvj402p图像数据保存为jpg图片

三、实现代码

/**
     * 打开视频流或者视频文件,并解码视频帧为YUVJ420P数据
     *
     * @param url -视频源地址
     * @param out_file 截图文件保存地址
     * @author eguid
     * @throws IOException
     */
    private int openVideo(String url,String out_file) throws IOException {
        AVFormatContext pFormatCtx = new AVFormatContext(null);
        int i, videoStream;
        AVCodecContext pCodecCtx = null;
        AVCodec pCodec = null;
        AVFrame pFrame = null;
        AVPacket packet = new AVPacket();
        int[] frameFinished = new int[1];
        AVDictionary optionsDict = null;

AVFrame pFrameRGB = null;
        int numBytes;
        BytePointer buffer = null;
        SwsContext sws_ctx = null;

// Open video file

if (avformat_open_input(pFormatCtx, url, null, null) != 0) {
            return -1; // Couldn't open file
        }

// Retrieve stream information
        if (avformat_find_stream_info(pFormatCtx, (PointerPointer<Pointer>) null) < 0) {
            return -1;// Couldn't find stream information
        }

av_dump_format(pFormatCtx, 0, url, 0);// Dump information about file onto standard error

// Find the first video stream
        videoStream = -1;
        for (i = 0; i < pFormatCtx.nb_streams(); i++) {
            if (pFormatCtx.streams(i).codec().codec_type() == AVMEDIA_TYPE_VIDEO) {
                videoStream = i;
                break;
            }
        }
        if (videoStream == -1) {
            return -1; // Didn't find a video stream
        }

// Get a pointer to the codec context for the video stream
        pCodecCtx = pFormatCtx.streams(videoStream).codec();

// Find the decoder for the video stream
        pCodec = avcodec_find_decoder(pCodecCtx.codec_id());
        if (pCodec == null) {
            System.err.println("Unsupported codec!");
            return -1; // Codec not found
        }
        // Open codec
        if (avcodec_open2(pCodecCtx, pCodec, optionsDict) < 0) {
            return -1; // Could not open codec
        }

pFrame = av_frame_alloc();// Allocate video frame

// Allocate an AVFrame structure
        pFrameRGB = av_frame_alloc();
        if (pFrameRGB == null) {
            return -1;
        }
        int width = pCodecCtx.width(), height = pCodecCtx.height();
        pFrameRGB.width(width);
        pFrameRGB.height(height);
        pFrameRGB.format(AV_PIX_FMT_YUVJ420P);
        // Determine required buffer size and allocate buffer
        numBytes = avpicture_get_size(AV_PIX_FMT_YUVJ420P, width, height);

buffer = new BytePointer(av_malloc(numBytes));

sws_ctx = sws_getContext(pCodecCtx.width(), pCodecCtx.height(), pCodecCtx.pix_fmt(), pCodecCtx.width(),
                pCodecCtx.height(), AV_PIX_FMT_YUVJ420P, SWS_BICUBIC, null, null, (DoublePointer) null);

// Assign appropriate parts of buffer to image planes in pFrameRGB
        // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
        // of AVPicture
        avpicture_fill(new AVPicture(pFrameRGB), buffer, AV_PIX_FMT_YUVJ420P, pCodecCtx.width(), pCodecCtx.height());

// Read frames and save first five frames to disk
        
        int ret=-1;
        while (av_read_frame(pFormatCtx, packet) >= 0) {
            if (packet.stream_index() == videoStream) {// Is this a packet from the video stream?
                avcodec_decode_video2(pCodecCtx, pFrame, frameFinished, packet);// Decode video frame

// Did we get a video frame?
                if (frameFinished != null) {
                    // 转换图像格式,将解压出来的YUV420P的图像转换为YUVJ420P的图像
                    sws_scale(sws_ctx, pFrame.data(), pFrame.linesize(), 0, pCodecCtx.height(), pFrameRGB.data(),
                            pFrameRGB.linesize());
                }

if (frameFinished[0] != 0 && !pFrame.isNull()) {
                    // Convert the image from its native format to YUVJ420P
                    sws_scale(sws_ctx, pFrame.data(), pFrame.linesize(),
0, pCodecCtx.height(), pFrameRGB.data(),pFrameRGB.linesize());
                    if((ret=saveImg(pFrameRGB,out_file))>=0) {
                        break;
                    }
                }
            }

}
        av_free_packet(packet);// Free the packet that was allocated by av_read_frame
        // Free the RGB image
        av_free(buffer);

av_free(pFrameRGB);

av_free(pFrame);// Free the YUV frame

avcodec_close(pCodecCtx);// Close the codec

avformat_close_input(pFormatCtx);// Close the video file

return ret;
    }

/**
     * 把YUVJ420P数据编码保存成jpg图片
     *
     * @param pFrame -图像帧
     * @param out_file -截图文件保存地址
     * @author eguid
     * @return
     */
    private int saveImg(AVFrame pFrame, String out_file) {
        AVPacket pkt = null;
        AVStream pAVStream = null;
        AVCodec codec = null;
        int ret = -1;

int width = pFrame.width(), height = pFrame.height();
        // 分配AVFormatContext对象
        AVFormatContext pFormatCtx = avformat_alloc_context();
        // 设置输出文件格式
        pFormatCtx.oformat(av_guess_format("mjpeg", null, null));
        if (pFormatCtx.oformat() == null) {
            return -1;
        }
        try {
            // 创建并初始化一个和该url相关的AVIOContext
            AVIOContext pb = new AVIOContext();
            if (avio_open(pb, out_file, AVIO_FLAG_READ_WRITE) < 0) {// dont open file
                return -1;
            }
            pFormatCtx.pb(pb);
            // 构建一个新stream
            pAVStream = avformat_new_stream(pFormatCtx, codec);
            if (pAVStream == null) {
                return -1;
            }
            int codec_id = pFormatCtx.oformat().video_codec();
            // 设置该stream的信息
            // AVCodecContext pCodecCtx = pAVStream.codec();
            AVCodecContext pCodecCtx = pAVStream.codec();
            pCodecCtx.codec_id(codec_id);
            pCodecCtx.codec_type(AVMEDIA_TYPE_VIDEO);
            pCodecCtx.pix_fmt(AV_PIX_FMT_YUVJ420P);
            pCodecCtx.width(width);
            pCodecCtx.height(height);
            pCodecCtx.time_base().num(1);
            pCodecCtx.time_base().den(25);

// Begin Output some information
            av_dump_format(pFormatCtx, 0, out_file, 1);
            // End Output some information

// 查找解码器
            AVCodec pCodec = avcodec_find_encoder(codec_id);
            if (pCodec == null) {// codec not found
                return -1;
            }
            // 设置pCodecCtx的解码器为pCodec
            if (avcodec_open2(pCodecCtx, pCodec, (PointerPointer<Pointer>) null) < 0) {
                System.err.println("Could not open codec.");
                return -1;
            }

// Write Header
            avformat_write_header(pFormatCtx, (PointerPointer<Pointer>) null);

// 给AVPacket分配足够大的空间
            pkt = new AVPacket();
            if (av_new_packet(pkt, width * height * 3) < 0) {
                return -1;
            }
            int[] got_picture = { 0 };
            // encode
            if (avcodec_encode_video2(pCodecCtx, pkt, pFrame, got_picture) >= 0) {
                // flush
                if ((ret = av_write_frame(pFormatCtx, pkt)) >= 0) {
                    // Write Trailer
                    if (av_write_trailer(pFormatCtx) >= 0) {
                        System.err.println("Encode Successful.");
                    }
                }
            }
            return ret;
            // 结束时销毁
        } finally {
            if (pkt != null) {
                av_free_packet(pkt);
            }
            if (pAVStream != null) {
                avcodec_close(pAVStream.codec());
            }
            if (pFormatCtx != null) {
                avio_close(pFormatCtx.pb());
                avformat_free_context(pFormatCtx);
            }
        }
    }

四、测试

使用了这个地址测试了截图效果:http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8"

还不错

javacpp-FFmpeg系列之1:视频拉流解码成YUVJ420P,并保存为jpg图片的更多相关文章

  1. javacpp-FFmpeg系列之2:通用拉流解码器,支持视频拉流解码并转换为YUV、BGR24或RGB24等图像像素数据

    javacpp-ffmpeg系列: javacpp-FFmpeg系列之1:视频拉流解码成YUVJ420P,并保存为jpg图片 javacpp-FFmpeg系列之2:通用拉流解码器,支持视频拉流解码并转 ...

  2. iOS - 直播流程,视频推流,视频拉流,简介,SMTP、RTMP、HLS、 PLPlayerKit

    收藏笔记 1 . 音视频处理的一般流程: 数据采集→数据编码→数据传输(流媒体服务器) →解码数据→播放显示1.数据采集:摄像机及拾音器收集视频及音频数据,此时得到的为原始数据涉及技术或协议:摄像机: ...

  3. 【转】直播流程,视频推流,视频拉流,简介,SMTP、RTMP、HLS、 PLPlayerKit

    原:https://www.cnblogs.com/baitongtong/p/11248966.html 1 .音视频处理的一般流程: 数据采集→数据编码→数据传输(流媒体服务器) →解码数据→播放 ...

  4. 视频拉流 Linux安装FFmpeg

    1 下载最新源码包并解压 $ wget http://ffmpeg.org/releases/ffmpeg-3.1.3.tar.bz2 $ tar jxvf ffmpeg-.tar.bz2 2安装ya ...

  5. 基于rtmp+nginx 、vlc实现FFmpeg推流与wpf端拉流

    这周在研究基于rtmp+nginx直播流的实现,现总结如下: 0.所需文件: 链接:https://pan.baidu.com/s/1U5gsNI8Rcl684l5gVL6swg 提取码:dli9 1 ...

  6. 海康&大华&DSS视频拉流-RTSP转RTMP多媒体播放技术

    海康&大华&DSS获取RTSP 实时流 海康:rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/ ...

  7. Android使用FFMpeg实现推送视频直播流到服务器

    背景 在过去的2015年中,视频直播页的新宠无疑是户外直播.随着4G网络的普及和覆盖率的提升,主播可以在户外通过手机进行直播.而观众也愿意为这种可以足不出户而观天下事的服务买单.基于这样的背景,本文主 ...

  8. 用ffmpeg命令将264裸码流封装成mp4(转载)

    转自:http://bbs.csdn.net/topics/370256130 ffmpeg -f h264 -i source.264 -vcodec copy out.mp4

  9. javacpp-FFmpeg系列补充:FFmpeg拉流截图实现在线演示demo(视频截图并返回base64图像,支持jpg/png/gif/bmp等多种格式)

    javacpp-ffmpeg系列: javacpp-FFmpeg系列之1:视频拉流解码成YUVJ420P,并保存为jpg图片 javacpp-FFmpeg系列之2:通用拉流解码器,支持视频拉流解码并转 ...

随机推荐

  1. IE8 "开发人员工具" 无法使用,无法显示

    经常使用IE8开发工具的开发人员可能会遇到这么一种去情况:按F12时任务栏里出现开发人员工具的任务,但是开发人员工具窗体不弹出,也不出现在IE8里,重装IE88后还是存在此问题. 解决办法其实非常简单 ...

  2. x264代码剖析(十三):核心算法之帧间预測函数x264_mb_analyse_inter_*()

    x264代码剖析(十三):核心算法之帧间预測函数x264_mb_analyse_inter_*() 帧间预測是指利用视频时间域相关性,使用临近已编码图像像素预測当前图像的像素,以达到有效去除视频时域冗 ...

  3. selenium3 踩坑--move_to_element()报错

    问题:selenium3 使用move_to_element()报错,报错信息如下图所示: 网上没有找到合适的解决办法,回退到稳定的selenium2可以解决. pip install seleniu ...

  4. mnesia的脏读和事物读的测试

    在mnesia中,有脏读脏写等以及事物读写,它们的差异通过测试不难发现: 代码如下: -module(mnesia_read_test). -compile(export_all). -record( ...

  5. linux环境tomcat配置及hadoop 2.6伪分布模式安装配置

    一.ubuntu 15.04.openjdk1.7.tomcat7环境配置 1. 配置openjdk1.7,输入命令: -jdk 2. 查看java是否安装成功,输入命令: envjava -vers ...

  6. Linux 批量替换的一种实现方式

    替换某目录下所有文件中的某个字符: sed -i 's/origin_str/new_str/g' `grep origin_str -rl ./` origin_str:被替换的字符串: new_s ...

  7. Unity3D 动态地创建识别图

    前面介绍了EasyAR的单图识别,它是提前在Unity设置好图片路径的,那么如果我们的图片是存储在服务器上的,那么我们肯定不能直接把服务的图片地址填上去了.这个时候我们可以动态地创建识别图.步骤如下: ...

  8. 【BZOJ3451】Tyvj1953 Normal 点分治+FFT+期望

    [BZOJ3451]Tyvj1953 Normal Description 某天WJMZBMR学习了一个神奇的算法:树的点分治!这个算法的核心是这样的:消耗时间=0Solve(树 a) 消耗时间 += ...

  9. Java 8 default 函数

    我们知道在java8之前 ,一个类实现一个接口需要实现接口所有的方法, 但是这样会导致一个问题,当一个接口有很多的实现类的时候,修改这个接口就变成了一个非常麻烦的事,需要修改这个接口的所有实现类 不过 ...

  10. 题解 P1095 【守望者的逃离】

    贪心.数组都不用开那种. 考虑跑步距离的构成.发现跑步只有三种情况构成 休息 传送 朴素地跑 显然,如果可以传送,我们就不要朴素地跑步.因为\(17\le 60 \div 2 =30\). 假如我们知 ...