2017-10-16 147 views
1

我是ffmpeg的新手。我使用FFmpegFrameGrabber和JavaCPP(版本1.3.3)來抓取mp4視頻文件的數據包。我想將數據包的字節流保存在數據庫中,以便在請求數據時解碼數據包並處理圖像。Java FFmpeg解碼avcodec_decode_video2負AVPacket結果

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

你不檢查'AVPacket.stream_index'是一個有效的'AVMEDIA_TYPE_VIDEO'流。 – WLGfx

+0

謝謝你的建議。我更新了這個問題,但仍然是同樣的錯誤。 –

回答

1

對不起,我只知道在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(); 

裸事實術語「串」在這裏包含的是嚇到我,因爲大多數字符串函數往往會做一些字節轉換和視頻的東西,我們正在處理二進制數據。

請讓我知道的任何問題或您的解決方案,我在這一個莫名其妙感興趣...

附:你可能可能只需要讀取更多的數據,而不僅僅是一個單獨的塊來獲取avcodec_decode_video2的視頻幀。不要指望視頻中的第一幀是Iframe。嘗試讓您的程序從文件的開頭到結尾讀取,然後專注於我上面寫的內容

編輯2:嘗試將輸入格式化爲原始h264流以便測試您的程序,例如,使用ffmpeg -i test.mp4 -vcodec copy -an c:\ temp \ test.h264

+0

非常感謝您的回答!在輸入之前將輸入格式化爲原始h264流。問題似乎是輸入是AVC1(沒有開始代碼的H.264比特流),avcodec_decode_video2需要開始代碼爲{0,0,0,1}的H.264比特流。雖然之前改變輸入爲我工作,我試圖找出什麼是最好的解決方案。也許有一種方法可以在運行時將AVPacket轉換爲帶有啓動代碼的比特流。像h264_mp4toannexb比特流過濾器聽起來很有前途... –

1

上面的答案適用於我。此外,我要發佈類似解決方案的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."); 
} 
+0

張貼最終代碼的感謝,非常感謝您分享它! – Harry