Java FFmpeg 解码 AVPacket 结果 avcodec_decode_video2 否定

Java FFmpeg decoding AVPacket result of avcodec_decode_video2 negative

我是 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] No start code is found.

[h264 @ 00000000214d11a0] Error splitting the input into NAL units.

以下是输入的一些元数据:

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

抱歉,我只知道 c++ 中的 ffmpeg,但不知道您的 java 东西。 你的问题是你不清楚你实际将什么提供给 mpeg4 解码器。您只能将原始 mpeg4 流输入其中。

我的建议是了解 libavcodec 的典型 C 类型用法与您的 java 之间的区别。

在 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();  

这里包含术语 "String" 的赤裸裸的事实让我感到害怕,因为大多数字符串函数倾向于进行一些字节转换,而在我们处理二进制数据的视频内容上。

请让我知道任何问题或您的解决方案,我对这个很感兴趣...

P.S。您可能只需要读取比单个块更多的数据来获取 avcodec_decode_video2 的视频帧。不要期望视频中的第一帧是 Iframe。在你专注于我上面写的内容之前,尝试让你的程序从文件的开头读到结尾

编辑 2:尝试将您的输入格式化为原始 h264 流以测试您的程序,例如使用 ffmpeg -i test.mp4 -vcodec copy -an c:\temp\test.h264

上面的答案对我有用。另外我想 post 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.");
}