第六節ffplay界面開發
作者:[email protected]
部落格:http://www.weibo.com/xdpan
工程位址:http://code.google.com/p/kudou-player/
--------------------------------------------
Android界面開發,屬于android開發範疇,我這裡沒有太多可以說的,簡單說寫我修改過的地方。還是使用第四節的android工程,主要修改聲音控制和按鍵控制,由于本人Android開發經驗有限,有些可能不太正确,也暫時放在文章中,待日後糾正。
修改
1, 去掉Activity的內建關系,修改構造函數(這個類我不想作為界面顯示)
static {
System.loadLibrary("ffmpeg");
System.loadLibrary("SDL");
System.loadLibrary("main");
}
SDLActivity(Context context, Handlerhandler,String filename){
mSingleton = this;
mFilename = filename;
mSurface = new SDLSurface(context);
mHandler = handler;
SurfaceHolder holder =mSurface.getHolder();
}
2, 添加notify函數(第五節中的提到的)
public static void notify(intmsg){
Log.v("SDL", "notify:" + msg);
mHandler.sendEmptyMessage(msg);
}
3, 增加jni的調用接口(第五節中的接口)
public static native int PlayerInit();
public static native intPlayerPrepare(String url);
public static native int PlayerMain();
public static native int PlayerExit();
public static native int PlayerSeekTo(intmsec);
public static native int PlayerPause();
public static native int PlayerIsPlay();
public static native intPlayerGetDuration();
public static native intPlayergetCurrentPosition();
4, 添加PlayerInit()到startApp()中,添加SDLActivity.PlayerPrepare(mFile)和SDLActivity.PlayerMain()到SDLMain中如下:
class SDLMainimplements Runnable {
private String mFile;
SDLMain(String f){
mFile = f;
}
public void run() {
SDLActivity.nativeInit();
SDLActivity.PlayerPrepare(mFile);
SDLActivity.PlayerMain();
//Log.v("SDL", "SDLthread terminated");
}
}
5,聲音和按鍵控制的修改如下
public boolean onKey(View v, int keyCode, KeyEvent event) {
Log.v("SDL", "keydown: " + keyCode + "aciton:" + event.getAction());
if (keyCode ==KeyEvent.KEYCODE_VOLUME_UP) {
VolumeUp();
return true;
}
else if (keyCode ==KeyEvent.KEYCODE_VOLUME_DOWN) {
VolumeDown();
return true;
}
return false;
}
public void VolumeUp(){
mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_RAISE, AudioManager.FX_FOCUS_NAVIGATION_UP);
}
public void VolumeDown(){
//mAudioManager.adjustVolume(AudioManager.ADJUST_RAISE, 0);
mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_LOWER, AudioManager.FX_FOCUS_NAVIGATION_UP);
}
界面編寫就不再解釋,隻給出代碼。
工程目錄如下:
主要播放代碼如下:
package com.kudou.player;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.GestureDetector.OnGestureListener;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import org.libsdl.app.SDLActivity;
import org.libsdl.app.SDLSurface;
public classKudouPlayer extends Activityimplements OnGestureListener {
private StringTAG="TestPlayer";
private SDLActivitymSoftPlayer=null;
private ViewmHideContainer;
private SeekBarmSeekBar;
private TextViewcurrentTime,totalTime;
private ViewimgPlay;
private StringfileName="/sdcard/test_1.mp4";
privateinttotalDuration = 0;
private Handlerhandler;
private SeekUpdaterseekUpdater=null;
private GestureDetectorgestureDetector =null;
publicfinalintMSG_LOAD_FINISHED= 10;
publicfinalintMSG_LOAD_UNFINISHED= 11;
publicfinalintMSG_OPEN_ERROR= 12;
publicfinalintMSG_OPEN_OK= 13;
publicfinalintMSG_SEEK_UPDATE= 30;
@Override
protectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ==================設定全屏=========================
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// ===================設定亮度========================
WindowManager.LayoutParamslp = getWindow().getAttributes();
lp.screenBrightness = 0.5F;
setContentView(R.layout.player);
FrameLayoutframeContainer = (FrameLayout) findViewById(R.id.framecontainer);
findViews();
gestureDetector = newGestureDetector(this);
handler = newHandler() {
publicvoidhandleMessage(Message msg) {
if (!Thread.currentThread().isInterrupted()){
System.out.println("receivemsg : " + msg.what);
switch (msg.what){
caseMSG_OPEN_OK:
startPlayer();
break;
caseMSG_OPEN_ERROR:
break;
caseMSG_LOAD_FINISHED:
break;
caseMSG_LOAD_UNFINISHED:
break;
caseMSG_SEEK_UPDATE:
Globals.ShowLog("MSG_SEEK_UPDATE");
if (seekUpdater!=null)
seekUpdater.refresh();
break;
}
}
super.handleMessage(msg);
}
};
UritmpUri = (Uri)this.getIntent().getData();
if (tmpUri != null){
fileName = tmpUri.getPath();
}else {
Bundlebundle =this.getIntent().getExtras();
if (bundle !=null){
fileName = bundle.getString("PATH");
}
}
mSoftPlayer = newSDLActivity(getApplication(),handler,fileName);
SDLSurfacesurface =mSoftPlayer.getSDLSurface();
FrameLayout.LayoutParamsparams =newFrameLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
params.gravity = Gravity.CENTER;
surface.setLayoutParams(params);
frameContainer.addView(surface);
}
publicvoidstartPlayer() {
totalDuration = mSoftPlayer.getDuration();
totalTime.setText(formatTime(totalDuration / 1000));
if (seekUpdater==null) {
seekUpdater =newSeekUpdater();
seekUpdater.startIt();
}
}
publicvoidshowLog(String s) {
Log.i(TAG, s);
}
public String formatTime(long sec) {
int h = (int)sec / 3600;
int m = (int)(sec % 3600) / 60;
int s = (int)sec % 60;
if (h == 0)
return String.format("%02d:%02d", m, s);
else
return String.format("%d:%02d:%02d", h, m, s);
}
publicvoidfindViews() {
mSeekBar = (SeekBar) findViewById(R.id.progressbar);
currentTime = (TextView) findViewById(R.id.currenttime);
totalTime = (TextView) findViewById(R.id.totaltime);
imgPlay = findViewById(R.id.img_vp_play);
imgPlay.setOnClickListener(imgPlayListener);
mSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener);
mHideContainer = findViewById(R.id.hidecontainer);
mHideContainer.setOnClickListener(mVisibleListener);
}
OnClickListenerimgPlayListener =new OnClickListener() {
@Override
public voidonClick(View v) {
ImageViewimg = (ImageView) v;
SDLActivitysp =mSoftPlayer;
if (sp !=null){
if (sp.isPlaying()) {
img.setImageResource(R.drawable.vp_play);
sp.stop();
}else {
img.setImageResource(R.drawable.vp_pause);
sp.start();
}
}
}
};
OnSeekBarChangeListenermSeekBarChangeListener =new OnSeekBarChangeListener() {
@Override
public voidonStopTrackingTouch(SeekBar seekBar) {
int totalTime, seekTo = 0;
int progress = seekBar.getProgress();
SDLActivitysp =mSoftPlayer;
if (sp !=null){
totalTime= sp.getDuration();
seekTo= totalTime / 1000 * progress;
sp.seekTo(seekTo);
}
System.out.println("Seekedto new progress: " + seekTo);
}
@Override
public voidonProgressChanged(SeekBar seekBar,intprogress,
boolean fromUser) {
//TODO Auto-generated method stub
}
@Override
public voidonStartTrackingTouch(SeekBar seekBar) {
//TODO Auto-generated method stub
}
};
OnClickListenermVisibleListener =new OnClickListener() {
public voidonClick(View v) {
Log.i("Test","onClickmVisibleListener");
if ((mHideContainer.getVisibility()== View.GONE)
||(mHideContainer.getVisibility() == View.INVISIBLE)) {
if (seekUpdater!=null) {
seekUpdater.startIt();
seekUpdater.refresh();
}
mHideContainer.setVisibility(View.VISIBLE);
}else {
mHideContainer.setVisibility(View.INVISIBLE);
if (seekUpdater!=null)
seekUpdater.stopIt();
}
}
};
protectedvoidonPause() {
super.onPause();
mSoftPlayer.exit();
mSoftPlayer.onPause();
}
protectedvoidonResume() {
super.onResume();
mSoftPlayer.onResume();
}
protectedvoidonDestroy() {
super.onDestroy();
mSoftPlayer.onDestroy();
}
privateclassSeekUpdater {
public voidstartIt() {
handler.sendEmptyMessage(MSG_SEEK_UPDATE);
}
public voidstopIt() {
handler.removeMessages(MSG_SEEK_UPDATE);
}
public voidrefresh() {
SDLActivitysp =mSoftPlayer;
if (currentTime!=null) {
long playedDuration = 0;
if (sp !=null)
playedDuration= sp.getCurrentPosition();
currentTime.setText(formatTime(playedDuration /1000));
if (totalDuration!= 0) {
int progress = (int)((1000 * playedDuration) /totalDuration);
mSeekBar.setProgress(progress);
}
}
handler.sendEmptyMessageDelayed(MSG_SEEK_UPDATE, 1000);
}
}
@Override
publicbooleanonTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
@Override
publicbooleanonDown(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}
@Override
publicbooleanonFling(MotionEvent e1, MotionEvent e2,floatvelocityX,
float velocityY) {
// TODO Auto-generated method stub
Log.i("Test","onFling");
return false;
}
@Override
publicvoidonLongPress(MotionEvent e) {
// TODO Auto-generated method stub
}
@Override
publicbooleanonScroll(MotionEvent e1, MotionEvent e2,floatdistanceX,
float distanceY) {
// TODO Auto-generated method stub
return false;
}
@Override
publicvoidonShowPress(MotionEvent e) {
// TODO Auto-generated method stub
}
@Override
publicbooleanonSingleTapUp(MotionEvent e) {
Log.i("Test","onSingleTapUp");
if ((mHideContainer.getVisibility()== View.GONE)
||(mHideContainer.getVisibility() == View.INVISIBLE)) {
if (seekUpdater!=null) {
seekUpdater.startIt();
seekUpdater.refresh();
}
mHideContainer.setVisibility(View.VISIBLE);
}else {
mHideContainer.setVisibility(View.INVISIBLE);
if (seekUpdater!=null)
seekUpdater.stopIt();
}
return false;
}
}
播放器截圖如下:

到此,android播放器就基本移植完成了,後面剩下的就是一些優化和完善工作了。
源工程如下(或者在虛拟機中對應目錄提取檔案):
kudou-player.zip