本篇文章的前一篇是采用FFmpeg解帧,并保持到JPG格式文件,其中使用了main函数对视频进行了解帧,本篇将针对Java如何调用来实现解帧并输出到文件。
一、使用VS2010将其改为Dll输出时,需要改变以下项目属性设置:
1、常规设置
2、库目录设置
3、预编译头设置
4、预处理设置
_INTEL WIN32 _DEBUG _CONSOLE _Windows _CRT_SECURE_NO_DEPRECATE
二、基于上篇文章代码我们添加如下函数声明以及实现
_declspec(dllexport) int findFKeyFrame(char *videoFilePath, char *frameFile); int findFKeyFrame(char *videoFilePath, char *frameFile) { AVFormatContext *pFormatCtx = NULL; int i,videoStream; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVFrame *pFrame; AVFrame *pFrameRGB; int numBytes; uint8_t *buffer; AVPacket packet; int frameFinished; struct SwsContext *img_convert_ctx = NULL; av_register_all(); // 检查参数以及视频能否解码 if(avformat_open_input(&pFormatCtx, videoFilePath, NULL, NULL)!=0) { printf("can not open"); return -1; } if(avformat_find_stream_info(pFormatCtx, NULL) < 0) { printf("can not find"); return -1; } av_dump_format(pFormatCtx, -1, videoFilePath, 0); 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 ) { printf("not find videoStream"); return -1; } pCodecCtx = pFormatCtx->streams[videoStream]->codec; pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if ( pCodec == NULL ) { return -1; } if( avcodec_open2(pCodecCtx, pCodec, NULL) < 0 ) { return -1; } pFrame = av_frame_alloc(); if( pFrame == NULL ) { return -1; } pFrameRGB = av_frame_alloc(); if( pFrameRGB == NULL ) { return -1; } // 根据视频宽高设置帧大小 numBytes = avpicture_get_size(PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height); buffer = av_malloc(numBytes); avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); img_convert_ctx = sws_getCachedContext(img_convert_ctx, pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL); i = 0; while( av_read_frame(pFormatCtx, &packet) >= 0 ) { if( packet.stream_index == videoStream ) { avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); if ( frameFinished && pFrame->key_frame) { if (!img_convert_ctx ) { fprintf(stderr, "Cannot initialize sws conversion context\n"); exit(1); } sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); if(i++<1) { saveFrameJpg(pFrameRGB->data[0],frameFile, i, pCodecCtx->width, pCodecCtx->height); } } } if(i > 5) { break; } } av_free_packet(&packet); av_free(buffer); av_free(pFrame); av_free(pFrameRGB); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); return 1; }
编译项目,生成DLL,并拷贝其到Java调用类目录下。
三、编写Java代码调用DLL
Java调用dll,肯定会用到JNI,但是此处使用比较常用地JNA框架来调用。JNA相关链接
可从test包可以查看怎么使用指针类Pointer等等
JNA代码实现如下:
package com.xiva.test.pic;
import java.io.UnsupportedEncodingException;
import com.sun.jna.Library;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
/**
*
* <调用DLL读取视频的第一关键帧,并以JPG格式输出到文件>
* @author xiva
* @version [1.0, 2014-5-15]
* @see [相关类/方法]
* @since [产品、模块版本]
*/
public class ReadVideoKeyFrame
{
public interface VideoDecoder extends Library {
/**
* 当前路径是在项目下,而不是bin输出目录下。
*/
VideoDecoder INSTANCE = (VideoDecoder) Native.loadLibrary("VideoDecoder", VideoDecoder.class);
public NativeLong findFKeyFrame(Pointer videoFilePath, Pointer frameFile);
}
public static void main(String[] args) throws UnsupportedEncodingException
{
final String ENCODING = "utf8";
String fileName = "E:\\frame01.jpg";
String path = "E:\\zookeeper.avi";
Pointer videoFilePath = new Memory(path.getBytes(ENCODING).length+1);
Pointer frameFile = new Memory(fileName.getBytes(ENCODING).length+1);
videoFilePath.setString(0, path);
frameFile.setString(0, fileName);
NativeLong ret = VideoDecoder.INSTANCE.findFKeyFrame(videoFilePath, frameFile);
System.out.println(ret);
}
}
运行程序,可以看到解帧成功。
同样此处还是未解决如何集成其他地解码器,毕竟ffmpeg地解码能力有限;需要后续再次扩展。