2011-05-31 88 views
9

我與xuggle工作,因爲一個星期,我寫了一個方法,通過視頻獲得 框架,但如果視頻是長這個方法需要太多的時間最佳方法:爪哇 - Xuggle - 獲得一個框架

public static void getFrameBySec(IContainer container, int videoStreamId, IStreamCoder videoCoder, IVideoResampler resampler, double sec) 
{ 
    BufferedImage javaImage = new BufferedImage(videoCoder.getWidth(), videoCoder.getHeight(), BufferedImage.TYPE_3BYTE_BGR); 
    IConverter converter = ConverterFactory.createConverter(javaImage, IPixelFormat.Type.BGR24); 
    IPacket packet = IPacket.make(); 
    while(container.readNextPacket(packet) >= 0) 
    { 
     if (packet.getStreamIndex() == videoStreamId) 
     { 
      IVideoPicture picture = IVideoPicture.make(videoCoder.getPixelType(), videoCoder.getWidth(), videoCoder.getHeight()); 
      int offset = 0; 
      while(offset < packet.getSize()) 
      { 
       int bytesDecoded = videoCoder.decodeVideo(picture, packet, offset); 
       if (bytesDecoded < 0) 
        throw new RuntimeException("got error decoding video"); 
       offset += bytesDecoded; 
       if (picture.isComplete()) 
       { 
        IVideoPicture newPic = picture; 
        if (resampler != null) 
        { 
         newPic = IVideoPicture.make(resampler.getOutputPixelFormat(), picture.getWidth(), picture.getHeight()); 

         if (resampler.resample(newPic, picture) < 0) 
          throw new RuntimeException("could not resample video from"); 
        } 
        if (newPic.getPixelType() != IPixelFormat.Type.BGR24) 
          throw new RuntimeException("could not decode video as RGB 32 bit data in"); 

        javaImage = converter.toImage(newPic); 
        try 
        { 
         double seconds = ((double)picture.getPts())/Global.DEFAULT_PTS_PER_SECOND; 
         if (seconds >= sec && seconds <= (sec +(Global.DEFAULT_PTS_PER_SECOND))) 
         { 

          File file = new File(Config.MULTIMEDIA_PATH, "frame_" + sec + ".png"); 
          ImageIO.write(javaImage, "png", file); 
          System.out.printf("at elapsed time of %6.3f seconds wrote: %s \n", seconds, file); 
          return; 
         } 
        } 
        catch (Exception e) 
        { 
         e.printStackTrace(); 
        } 
       } 
      } 
     } 
     else 
     { 
      // This packet isn't part of our video stream, so we just 
      // silently drop it. 
     } 
    } 
    converter.delete(); 
} 

你知道更好的方法嗎?

回答

2

那麼從閱讀你的代碼,我看到了一些可以優化的東西。

您首先通讀整個文件一次,創建一個byteoffsets和seconds的索引。然後,函數可以從給定的秒數查找byteoffset,並且可以在該偏移量處解碼視頻,並執行其餘的代碼。

另一種選擇是使用你的方法,每次讀取整個文件,但不是調用所有重新採樣器,newPic和Java圖像轉換器代碼,檢查秒是否匹配。如果他們這樣做,然後將圖像轉換成新的圖片來顯示。

所以

if(picture.isComplete()){ 

try { 

    double seconds = ((double)picture.getPts())/Global.DEFAULT_PTS_PER_SECOND; 
    if (seconds >= sec && seconds <= (sec +(Global.DEFAULT_PTS_PER_SECOND))) 
    { 
    Resample image 
    Convert Image 
    Do File stuff 
    } 
2

使用seekKeyFrame選項。您可以使用此功能來查看視頻文件中的任何時間(時間以毫秒爲單位)。

double timeBase = 0; 
int videoStreamId = -1; 

private void seekToMs(IContainer container, long timeMs) { 
    if(videoStreamId == -1) { 
     for(int i = 0; i < container.getNumStreams(); i++) { 
      IStream stream = container.getStream(i); 
      IStreamCoder coder = stream.getStreamCoder(); 
      if (coder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) { 
       videoStreamId = i; 
       timeBase = stream.getTimeBase().getDouble(); 
       break; 
      } 
     } 
    } 

    long seekTo = (long) (timeMs/1000.0/timeBase); 
    container.seekKeyFrame(videoStreamId, seekTo, IContainer.SEEK_FLAG_BACKWARDS); 
} 

從那裏你可以使用你的經典while(container.readNextPacket(packet) >= 0)獲取圖像文件的方法。

注意:它不會尋求確切的時間,但近似,所以你仍然需要通過數據包(但當然比以前少得多)。

+0

什麼是「timeMs」或「seekTO」相對於這種情況?我不太明白..我該如何跳過目前的分數? [IVideoPicture.getPts()] – KarlP 2015-06-09 17:08:48