Androidの特定のビデオファイルのキーフレームのタイムスタンプのリストを提供できるAPIはありますか?
5 に答える
エレガントなソリューションの場合、これを実現するには、Android用のffmpegを使用する必要があるようです。他の誰かがそのようにそれを試しました: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をインストールするのに手間をかけずに動作するでしょう...それはおそらくかなり遅いです(私は知りません)。しかし多分それはあなたが必要とするものだけです。
PS:あなたの問題に合ったffmpegチュートリアルはここで見つけることができます:http://dranger.com/ffmpeg/tutorial07.html
正解はhttp://code.google.com/p/mp4parser/だと思います
アイソパーサーAPIは、MP4ファイル構造の読み取りと書き込みを行うことができます。これは、いわゆるボックスを処理する低レベルのツールですが、トラックや映画などの構造も処理します。
素晴らしいAPIとffmpegbehemothよりもはるかに高速です
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);
}
@ryangordonのソリューションに基づいて構築します。seekTo
指定されている場合でも、同じフレームをシークする場合SEEK_TO_NEXT_SYNC
があります。これを回避するには、フレームの繰り返しが開始されたら終了できます。
例えば:
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 ;