本篇文章的前一篇是采用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地解码能力有限;需要后续再次扩展。