Java FFmpeg解码avcodec_decode_video2的AVPacket结果为负

时间:2017-10-16 12:32:16

标签: java ffmpeg h.264

我是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

2 个答案:

答案 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.");
}