我是ffmpeg的新手。我正在使用FFmpegFrameGrabber和JavaCPP(版本1.3.3)来获取mp4视频文件的数据包。我想将数据包的字节流保存在数据库中,以便在请求数据时解码数据包并处理图像。
public static void saveVideoToDB(String pathToVideo){
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(pathToVideo);
grabber.start();
AVPacket pkt;
while ((pkt = grabber.grabPacket()) != null) {
BytePointer data = pkt.data();
data.capacity(pkt.size());
byte[] arr = data.getStringBytes();
//arr = [0, 0, 0, 62, 39, 100, 0, 40, -83, -124.....]
if (pkt.stream_index() == AVMEDIA_TYPE_VIDEO) {
//ToDo: save arr to database
testDecode(arr);
}
}
grabber.close();
logger.info("Import video finished.");
}
在我的代码中,我首先尝试解码数据包的数据以进行概念验证,但我不确定它是否正常工作:
public static void testDecode(byte[] data){
AVCodec avCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
AVCodecContext avCodecContext = avcodec_alloc_context3(avCodec);
AVDictionary opts = new AVDictionary();
avcodec_open2(avCodecContext, avCodec, opts);
av_dict_free(opts);
AVFrame avFrame = av_frame_alloc();
AVPacket avPacket = new AVPacket();
av_init_packet(avPacket);
Frame frame = new Frame();
avPacket.pts(AV_NOPTS_VALUE);
avPacket.dts(AV_NOPTS_VALUE);
BytePointer bp = new BytePointer(data);
bp.capacity(data.length);
avPacket.data(bp);
avPacket.size(data.length);
avPacket.pos(-1);
IntBuffer gotPicture = IntBuffer.allocate(1);
boolean doVideo = true;
boolean keyFrames = false;
boolean processImage = true;
AVPacket pkt = avPacket;
if (doVideo) {
int len = avcodec_decode_video2(avCodecContext, avFrame, gotPicture, avPacket);
if (len >= 0 && gotPicture.get(0) != 0
&& (!keyFrames || avFrame.pict_type() == AV_PICTURE_TYPE_I)) {
//ToDo: process image
logger.info("decode success");
}else{
logger.info("decode failed");
}
}
}
avcodec_decode_video2的结果始终为负数(-1094995529 =>处理输入时发现无效数据),我收到以下错误:
[h264 @ 00000000214d11a0]找不到起始码。
[h264 @ 00000000214d11a0]将输入拆分为NAL单位时出错。
以下是输入中的一些元数据:
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'C:\Users\user01\Documents\fullstream.mp4':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp42mp41isomiso2
creation_time : 2017-07-27T11:17:19.000000Z
Duration: 00:55:55.48, start: 0.000000, bitrate: 5126 kb/s
Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080, 4996 kb/s, 25 fps, 25 tbr, 2500 tbn, 5k tbc (default)
Metadata:
creation_time : 2017-07-27T11:17:19.000000Z
handler_name : VideoHandler
Stream #0:1(eng): Audio: mp3 (mp4a / 0x6134706D), 24000 Hz, mono, s16p, 125 kb/s (default)
Metadata:
creation_time : 2017-07-27T11:17:19.000000Z
handler_name : SoundHandler
答案 0 :(得分:2)
抱歉,我只知道c ++中的ffmpeg而不是你的java东西。 你的问题是你不清楚你实际上为mpeg4解码器提供了什么。您只能将原始mpeg4流输入其中。
我的建议是了解libavcodec和java的典型C类型用法之间的区别。
在C中我们做了类似的事情,以确保只将视频二进制数据输入解码器:while(av_read_frame(pFormatCtx, &packet)>=0) {
// Is this a packet from the video stream?
if(packet.stream_index==videoStream) {
// Decode video frame
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
...
查看您的代码,我不知道FFmpegFrameGrabber如何映射到av_read_frame,请仔细查看您的函数调用
byte[] arr = data.getStringBytes();
这里包含术语“字符串”这一事实对我来说是可怕的,因为大多数字符串函数往往会进行一些字节转换,而对于视频内容我们处理的是二进制数据。
请让我知道任何问题或您的解决方案,我对此感兴趣......
P.S。你可能只需要读取比单个块更多的数据来获取avcodec_decode_video2的视频帧。不要指望视频中的第一帧是iframe。尝试让你的程序从文件的开头到结尾阅读,然后再集中精力于我上面写的内容
编辑2:尝试将您的输入格式化为原始h264流,以便测试您的程序,例如:使用ffmpeg -i test.mp4 -vcodec copy -an c:\ temp \ test.h264
答案 1 :(得分:2)
上面的答案对我有用。另外我想发布类似java的解决方案:添加AVBitStreamFilter就可以了:
public static void saveVideoToDB(String pathToVideo){
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(pathToVideo);
grabber.start();
//added this
AVStream v_stream = oc.streams(AVMEDIA_TYPE_VIDEO);
AVCodecContext codec = v_stream.codec();
AVBitStreamFilterContext h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
AVPacket pkt;
while ((pkt = grabber.grabPacket()) != null) {
//and this
av_apply_bitstream_filters(codec, pkt, h264bsfc);
BytePointer data = pkt.data();
data.capacity(pkt.size());
byte[] arr = data.getStringBytes();
if (pkt.stream_index() == AVMEDIA_TYPE_VIDEO) {
testDecode(arr);
}
}
av_bitstream_filter_close(h264bsfc);
grabber.close();
logger.info("Import video finished.");
}