[摘要:比来做的项目请求既能播放视频(近似于视频播放器),又能每隔1s摆布猎取一帧视频绘里,然后对图片举行处置惩罚,观察了一周,也被熬煎了一周,总算找到了大抵相符请求的方式。起首对]
最近做的项目要求既能播放视频(类似于视频播放器),又能每隔1s左右获取一帧视频画面,然后对图片进行处理,调查了一周,也被折磨了一周,总算找到了大致符合要求的方法。首先对调查过程中涉及到的方法进行简单介绍,再重点介绍最终所采用的方法,话不多说,进入正题。
一.MediaMetadataRetriever
播放视频并取得画面的一帧,大家最先想到应该都是这个,我同样也最先对它进行了测试,这里使用MediaPlayer进行播放,视频播放界面使用SurfaceView来实现。
public class PlayerMainActivity extends Activity implements OnClickListener,
SurfaceHolder.Callback, Runnable {
private static final String TAG = "Movie";
private MediaPlayer mediaPlayer;
private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private Button play_btn;private int currentPosition = 0;
private Bitmap bitmap = null;
private String dataPath = Environment.getExternalStorageDirectory() + "/Video/Test_movie.AVI";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
play_btn = (Button) findViewById(R.id.play_btn);
play_btn.setOnClickListener(this);
screen_cut_btn.setOnClickListener(this);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
}
@Override
public void run() {
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDisplay(surfaceHolder);
try {
mediaPlayer.setDataSource(dataPath);
mediaPlayer.prepare();
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(dataPath);
int millis = mediaPlayer.getDuration();
Log.i(TAG, "millis: " + millis/1000);
for (int i = 10000*1000; i < 20*1000*1000; i+=500*1000) {
bitmap = mediaMetadataRetriever.getFrameAtTime(i, MediaMetadataRetriever.OPTION_CLOSEST);
String path = Environment.getExternalStorageDirectory() + "/bitmap/" + i + ".png";
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(path);
bitmap.compress(CompressFormat.PNG, 100, fileOutputStream);
Log.i(TAG, "i: " + i/1000/1000);
} catch (Exception e) {
Log.i(TAG, "Error: " + i/1000/1000);
e.printStackTrace();
}
finally {
if (fileOutputStream != null) {
fileOutputStream.close();
}
}
bitmap.recycle();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Thread t = new Thread(this);
t.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.play_btn:
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
play_btn.setText(getResources().getText(R.string.play));
} else {
mediaPlayer.start();
play_btn.setText(getResources().getText(R.string.pause));
}
break;default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
mediaPlayer.release();
}
}
获取一帧的关键代码为:
Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(timeMs * 1000, MediaMetadataRetriever.OPTION_CLOSEST);
public Bitmap getFrameAtTime(long timeUs, int option)
第一个参数是传入时间,只能是us(微秒)
第二个参数:
OPTION_CLOSEST 在给定的时间,检索最近一个帧,这个帧不一定是关键帧。
OPTION_CLOSEST_SYNC 在给定的时间,检索最近一个同步与数据源相关联的的帧(关键帧)。
OPTION_NEXT_SYNC 在给定时间之后检索一个同步与数据源相关联的关键帧。
OPTION_PREVIOUS_SYNC 顾名思义,