如何在Android中获取视频文件的关键帧(同步帧)时间戳列表



是否有API可以为我提供Android中给定视频文件的关键帧的时间戳列表?

对于一个优雅的解决方案,似乎需要使用ffmpeg For android来实现这一点。其他人也尝试过这种方式:如何在android中从视频文件中获取帧;并建议使用ffmpeg。

我查看了参考手册,但只能找到一种方法,使用MediaMetadataRetriever()获得相对于给定位置的关键帧本身(没有时间戳),如下所述:http://developer.android.com/reference/android/media/MediaMetadataRetriever.html#extractMetadata%28int%29

如果你不想使用ffmpeg,你可以尝试以下方法:

private ArrayList<int> getKeyFrameTimestamps(String filename) {
  ArrayList<int> timeStamps = new ArrayList<int>();
  try {
    MediaMetadataRetriever mRetriever = new MediaMetadataRetriever();
    mRetriever.setDataSource(getDataSource(filename));
    mRetriever.getFrameAtTime(i, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
    Bitmap lastFame = mRetriever.getFrameAtTime(0, MediaMetadataRetriever.OPTION_NEXT_SYNC);
    result.add(0);
    for(int time=1; frame ; ++time) {
      Bitmap frame = mRetriever.getFrameAtTime(time, MediaMetadataRetriever.OPTION_PREVIOUS_SYNC);
      if (frame && !frame.sameAs(lastFrame)) {
        result.add(time);
        lastFrame = frame;
      }
    }
  } catch (Exception e) {  /* TODO: HANDLE THIS */ }
  return timeStamps;
}

这当然不是很优雅,但安装ffmpeg可能会毫不费力。。。它可能也很慢(我不知道)。但也许这正是你所需要的。

附言:适合您问题的ffmpeg教程可以在这里找到:http://dranger.com/ffmpeg/tutorial07.html

我相信正确答案是http://code.google.com/p/mp4parser/

等参线器API可以读取和写入MP4文件结构。这是一个处理所谓盒子的低级工具,但它也处理像曲目和电影这样的结构。

不错的API,比ffmpeg巨头快得多

4.1+-比mp4parser快,因为它是在原生底层完成的,代码更干净

mp4parser您必须根据getsyncsamples+getsampledurations计算时间戳,这也很烦人,而且它们与ffprobe不匹配。这得到了准确的结果

MediaExtractor extractor = new MediaExtractor();
extractor.setDataSource(getVideoPath());
int trackindex = MediaExtractorUtil.selectVideoTrack(extractor);
extractor.selectTrack(trackindex);
while (extractor.getSampleTime() != -1) {
    long sampleTime = extractor.getSampleTime();
    // check not really necessary but JIC
    if ((extractor.getSampleFlags() & MediaExtractor.SAMPLE_FLAG_SYNC) > 0) {
        mKeyframeTimestampsMS.add(sampleTime / 1000);
    }
    extractor.seekTo(sampleTime + 1, MediaExtractor.SEEK_TO_NEXT_SYNC);
}

基于@ryan-gordon的解决方案。即使在指定了SEEK_TO_NEXT_SYNC的情况下,seekTo也可以寻找相同的帧。为了避免这种情况,可以在帧开始重复时终止。

例如:

    private fun getSyncFrameTimestamps(source: MediaExtractor): List<Long> {
        val result = mutableListOf<Long>()
        var lastSampleTime = -1L
        var sampleTime = source.sampleTime
        while (sampleTime >= 0L && sampleTime != lastSampleTime) {
            if (source.sampleFlags and MediaExtractor.SAMPLE_FLAG_SYNC != 0) {
                result.add(sampleTime)
            }
            source.seekTo(sampleTime + 1L, MediaExtractor.SEEK_TO_NEXT_SYNC)
            lastSampleTime = sampleTime
            sampleTime = source.sampleTime
        }
        return result
    }

请注意,在调用此操作之前,source应设置数据源,并选择视频轨道。

使用MediaExtractor,您可以在任何所需时间拥有所有关键帧(同步)时间和/或任何特定关键帧+一些更有用的数据。

然后通过MediaMetadataRetriever->getFrameAtTime可以获得帧位图。

以下是使用MediaExtractor:的示例代码

    mediaExtractor = new MediaExtractor() ;
    mediaExtractor.setDataSource(mediaPath);
    int numTracks = mediaExtractor.getTrackCount();
    for (int i = 0; i < numTracks; ++i) {
        MediaFormat format = mediaExtractor.getTrackFormat(i);
        String mime = format.getString(MediaFormat.KEY_MIME);
        if ( mime.startsWith("video") ) {
            mediaExtractor.selectTrack(i);
        }
        Log.i( "LOG_Extractor" , "track "+i+" "+mime );
    }
    currentTime = mediaPlayer.getCurrentPosition();
    mediaExtractor.seekTo( (long)currentTime*1000 , MediaExtractor.SEEK_TO_PREVIOUS_SYNC );
    previousKeyFrameTimeMilis = mediaExtractor.getSampleTime() /1000 ;

最新更新