javacpp-FFmpeg系列之1:视频拉流解码成YUVJ420P,并保存为jpg图片
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图片的更多相关文章
- javacpp-FFmpeg系列之2:通用拉流解码器,支持视频拉流解码并转换为YUV、BGR24或RGB24等图像像素数据
javacpp-ffmpeg系列: javacpp-FFmpeg系列之1:视频拉流解码成YUVJ420P,并保存为jpg图片 javacpp-FFmpeg系列之2:通用拉流解码器,支持视频拉流解码并转 ...
- iOS - 直播流程,视频推流,视频拉流,简介,SMTP、RTMP、HLS、 PLPlayerKit
收藏笔记 1 . 音视频处理的一般流程: 数据采集→数据编码→数据传输(流媒体服务器) →解码数据→播放显示1.数据采集:摄像机及拾音器收集视频及音频数据,此时得到的为原始数据涉及技术或协议:摄像机: ...
- 【转】直播流程,视频推流,视频拉流,简介,SMTP、RTMP、HLS、 PLPlayerKit
原:https://www.cnblogs.com/baitongtong/p/11248966.html 1 .音视频处理的一般流程: 数据采集→数据编码→数据传输(流媒体服务器) →解码数据→播放 ...
- 视频拉流 Linux安装FFmpeg
1 下载最新源码包并解压 $ wget http://ffmpeg.org/releases/ffmpeg-3.1.3.tar.bz2 $ tar jxvf ffmpeg-.tar.bz2 2安装ya ...
- 基于rtmp+nginx 、vlc实现FFmpeg推流与wpf端拉流
这周在研究基于rtmp+nginx直播流的实现,现总结如下: 0.所需文件: 链接:https://pan.baidu.com/s/1U5gsNI8Rcl684l5gVL6swg 提取码:dli9 1 ...
- 海康&大华&DSS视频拉流-RTSP转RTMP多媒体播放技术
海康&大华&DSS获取RTSP 实时流 海康:rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/ ...
- Android使用FFMpeg实现推送视频直播流到服务器
背景 在过去的2015年中,视频直播页的新宠无疑是户外直播.随着4G网络的普及和覆盖率的提升,主播可以在户外通过手机进行直播.而观众也愿意为这种可以足不出户而观天下事的服务买单.基于这样的背景,本文主 ...
- 用ffmpeg命令将264裸码流封装成mp4(转载)
转自:http://bbs.csdn.net/topics/370256130 ffmpeg -f h264 -i source.264 -vcodec copy out.mp4
- javacpp-FFmpeg系列补充:FFmpeg拉流截图实现在线演示demo(视频截图并返回base64图像,支持jpg/png/gif/bmp等多种格式)
javacpp-ffmpeg系列: javacpp-FFmpeg系列之1:视频拉流解码成YUVJ420P,并保存为jpg图片 javacpp-FFmpeg系列之2:通用拉流解码器,支持视频拉流解码并转 ...
随机推荐
- Spring Data JPA 事务锁
1.概述 在本快速教程中,我们将讨论在Spring Data JPA中为自定义查询方法和预定义存储库的CRUD方法启用事务锁, 我们还将查看不同的锁类型并设置事务锁超时. 2.锁类型 JPA定义了两种 ...
- canvas drawImage方法不显示图片的解决方案
先复习一下用法: context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height); 各个参数说明: 参数 描述 img 规定要使用的图像.画布 ...
- 使用Erlang和Thrift,与Hbase通信(转)
操作系统是Ubuntu Server 12.10 先安装Thrift sudo apt-get install libboost-dev libboost-test-dev \ libboost-pr ...
- 【WPF】ComboBox:根据绑定选取、设置固定集合中的值
问题场景 我有一个对象,里面有一个属性叫Limit,int类型.虽然int可取的范围很大,我想要在用户界面上限制Limit可取的值,暂且限制为5.10.15.20. 所以ComboBox绑定不是绑定常 ...
- C#连接Oracle的问题(不安装客户端)
win7环境,本地没有安装oracle或者客户端,现在需要程序里连接远程oracle DB: 如果采用System.Data.OracleClient肯定是不行的,这个要安装客户端的: 所以就尝试O ...
- 高性能流媒体服务器EasyDSS前端重构(二) webpack + vue + AdminLTE 多页面提取共用文件, 优化编译时间
本文围绕着实现EasyDSS高性能流媒体服务器的前端框架来展开的,具体EasyDSS的相关信息可在:www.easydss.com 找到! 接上回 <高性能流媒体服务器EasyDSS前端重构(一 ...
- 在做RTSP摄像机H5无插件直播中遇到的对接海康摄像机发送OPTIONS心跳时遇到的坑
我们在实现一套EasyNVR无插件直播方案时,选择了采用厂家无关化的通用协议RTSP/Onvif接入摄像机IPC/NVR设备,总所周知,Onvif是摄像机的发现与控制管理协议,Onvif用到的流媒体协 ...
- 九度OJ 1049:字符串去特定字符 (基础题)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:8499 解决:3860 题目描述: 输入字符串s和字符c,要求去掉s中所有的c字符,并输出结果. 输入: 测试数据有多组,每组输入字符串s和 ...
- intellij idea同一个窗口打开多个项目
1 将多个maven项目放在同一个目录 2 file --> open该目录 intellij 将会加载该目录下所有的项目. 3 依赖加载 maven projects--->" ...
- 性能测试--Jmeter之wordpress示例
Jmeter之wordpress示例 WordPress是使用PHP语言开发的博客平台,用户可以在支持PHP和MySQL数据库的服务器上架设属于自己的网站.也可以把 WordPress当作一个内容管理 ...