1. 程式人生 > >Android多媒體之視訊播放器高階開發

Android多媒體之視訊播放器高階開發

1.獲取播放的資料來源

播放視訊的資料來源一般有兩個,一個是請求網路,從伺服器後臺直接獲取播放的視訊資訊,另一種是播放手機中本地的視訊,這裡我們採用的播放源為播放手機本地的視訊

1.1 查詢獲取手機中的視訊的資訊

1.1.1 查詢方法一

定義要查詢到的視訊的資訊,包括視訊的名稱,視訊大小,視訊播放時長以及在手機中的儲存位置

String[] projection = {Media._ID,Media.TITLE,Media.SIZE,Media.DURATION,Media.DATA};


進行資料的查詢操作
Cursor cursor = getActivity().getContentResolver().query(Media.EXTERNAL_CONTENT_URI, projection, null, null, null);

這裡查詢到的資訊是包含在cursor物件中的

1.1.2 查詢方法二

SimpleQueryHandler	queryHandler = new SimpleQueryHandler(getActivity().getContentResolver());
	
	String[] projection = {Media._ID,Media.TITLE,Media.SIZE,Media.DURATION,Media.DATA};
	
	queryHandler.startQuery(0, adapter, Media.EXTERNAL_CONTENT_URI, projection, null, null, null);

public class SimpleQueryHandler  extends AsyncQueryHandler{
		public SimpleQueryHandler(ContentResolver cr) {
			super(cr);
		}
		@Override
		protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
			super.onQueryComplete(token, cookie, cursor);
			if(cookie!=null && cookie instanceof CursorAdapter){
				CursorAdapter adapter = (CursorAdapter) cookie;
				adapter.changeCursor(cursor);//相當於notify
			}
		}
	}

邊裡是呼叫的非同步查詢的方法,查詢到的視訊的相關資訊也是包含在cursor中的,其中傳入的adapter物件是繼承於CursorAdapter

1.1.3 建立儲存視訊資訊的info物件

public class VideoItem implements Serializable{
		public String title;//視訊的名稱
		public long size;//視訊的大小
		public long duration;//視訊的時長
		public String path;//視訊在手機中的路徑
		
		/**
		 * 將cursor中的資料封裝為一個info
		 * @param cursor
		 * @return
		 */
		public static VideoItem fromCursor(Cursor cursor){
			VideoItem videoItem = new VideoItem();
			videoItem.duration=cursor.getLong(cursor.getColumnIndex(Media.DURATION)));
			videoItem.path=cursor.getString(cursor.getColumnIndex(Media.DATA)));
			videoItem.size=cursor.getLong(cursor.getColumnIndex(Media.SIZE)));
			videoItem.title=cursor.getString(cursor.getColumnIndex(Media.TITLE)));
			return videoItem;
		}
	}

2.以列表的方式將視訊資訊顯示出來

2.1 建立資料介面卡

這裡使用到的是ListView來設定顯示資料,所以需要給ListView準備一個介面卡,來設定顯示資料,由於我們查詢到的資料是封裝在Cursor中的,所以我們這裡可以建立繼承於CursorAdapter的介面卡

public class VideoListAdapter extends CursorAdapter{

		public VideoListAdapter(Context context, Cursor c) {
			super(context, c);
		}
		
		/**
		 * 直接從佈局中載入view返回,這裡就是ListView中顯示的item佈局,可以自定義隨便佈局
		 */
		@Override
		public View newView(Context context, Cursor cursor, ViewGroup parent) {
			return View.inflate(context, R.layout.adapter_video_list, null);
		}
		
		ViewHolder holder;
		/**
		 * 將資料設定給view
		 */
		@Override
		public void bindView(View view, Context context, Cursor cursor) {
			holder = getHolder(view);
			//獲取資料,這裡的資料是儲存在cursor中,通過方法fromCursor直接轉換成VideoItem物件儲存的資訊
			VideoItem videoItem = VideoItem.fromCursor(cursor);
			
			holder.tv_title.setText(videoItem.title);
			//這裡獲取到的時長是毫秒,可以通過 自定義的時間格式來對時間進行格式化操作
			holder.tv_duration.setText(StringUtil.formatVideoDuration(videoItem.duration()));
			//將視訊的大小格式化為M來進行資料顯示
			holder.tv_size.setText(Formatter.formatFileSize(context, videoItem.size()));
			
		}
		
		private ViewHolder getHolder(View view){
			ViewHolder viewHolder = (ViewHolder) view.getTag();
			if(viewHolder==null){
				viewHolder = new ViewHolder(view);
				view.setTag(viewHolder);
			}
			return viewHolder;
		}
		
		class ViewHolder{
			TextView tv_title,tv_duration,tv_size;
			public ViewHolder(View view){
				tv_title = (TextView) view.findViewById(R.id.tv_title);
				tv_duration = (TextView) view.findViewById(R.id.tv_duration);
				tv_size = (TextView) view.findViewById(R.id.tv_size);
			}
		}
   }


2.2 為ListView設定顯示資料

VideoListAdapter  adapter = new VideoListAdapter(getActivity(), null);
	listView.setAdapter(adapter);


注:到這裡,我們就 可以將手機中的視訊的資訊顯示到一個ListView中了

3.點選資訊條目,跳轉到視訊播放頁面

3.1 為ListView設定條目的點選事件,並獲取到相應位置的視訊資訊

listView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
			
				//獲取當前點選的條目對應的資料
				Cursor cursor = (Cursor) adapter.getItem(position);
				Bundle bundle = new Bundle();
				//當前點選播放視訊的位置
				bundle.putInt("currentPosition", position);
				//視訊資訊的集合
				bundle.putSerializable("videoList", cursorToList(cursor));
			
			Intent intent = new Intent(getActivity(),VitamioPlayerActivity.class);
		
			intent.putExtras(bundle);
		
			getActivity().startActivity(intent);
				
			}
		});

其中呼叫了一個方法,是將cursor中的資料轉換成集合中儲存物件的方法儲存資訊,以方便資料的傳遞
private ArrayList<VideoItem> cursorToList(Cursor cursor){
		ArrayList<VideoItem> list = new ArrayList<VideoItem>();
		cursor.moveToPosition(-1);
		while(cursor.moveToNext()){
			list.add(VideoItem.fromCursor(cursor));
		}
		return list;
	}

其中呼叫的VideoItem.fromCursor方法是物件中封裝的轉換cursor物件資料的方法

4.視訊播放頁面

4.1 獲取傳遞過來的視訊資訊

private int currentPosition;//當前播放視訊的位置
	private ArrayList<VideoItem> videoList;//當前的視訊列表
	
	
	currentPosition = getIntent().getExtras().getInt("currentPosition");
	videoList = (ArrayList<VideoItem>) getIntent().getExtras().getSerializable("videoList");

4.2 設定視訊播放並設定監聽

這裡使用到的是控制元件VideoView,

//設定播放的資料
	playVideo();
	//設定進行播放的操作監聽
	video_view.setOnPreparedListener(new OnPreparedListener() {
			@Override
			public void onPrepared(MediaPlayer mp) {
				//進行視訊的播放		
				video_view.start();
				//更新視訊播放指示的進度條
				updatePlayProgress();
				//設定指示的進度條的最大值
				video_seekbar.setMax((int) video_view.getDuration());
				//設定顯示當前的播放時間
				tv_current_position.setText("00:00");
				//設定顯示視訊的總時長
				tv_duration.setText(StringUtil.formatVideoDuration(video_view.getDuration()));
				//設定播放按鈕的背景圖片
				btn_play.setBackgroundResource(R.drawable.selector_btn_pause);
			}
		});
呼叫的方法playVideo
private void playVideo(){
	//上一視訊的控制按鈕(如果是第一節,則不再點選)
		btn_pre.setEnabled(currentPosition!=0);
	//下一視訊的控制按鈕(如果是最後一節,則不再點選)
		btn_next.setEnabled(currentPosition!=(videoList.size()-1));
	//獲取當前位置對應的資料
		VideoItem videoItem = videoList.get(currentPosition);
	//設定頁面顯示的視訊標題
		tv_name.setText(videoItem.getTitle());
	//進行視訊的播放
		video_view.setVideoURI(Uri.parse(videoItem.getPath()));
	}

playVideo方法為videoview設定了播放的資料來源,當videoView載入準備好後,由於設定了監聽OnPreparedListener方法,所以會呼叫其中的onPrepared,在這裡進行開始播放視訊的操作,並可以獲取到當前要播放的視訊的總時長,以設定指示進度的進度條顯示的最大值,以及更新其他相關的UI等

4.3 設定點選播放上一節視訊的方法

btn_pre.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				if(currentPosition>0){
				currentPosition - -;
				playVideo();
			}
				
			}
		});
btn_pre 是播放上一節視訊的觸發按鈕

這裡的currentPosition是當前播放的視訊在視訊集合中的位置,呼叫playVideo,會將對應位置的視訊資訊獲取出來,然後設定給VideoView,然後當資源準備完成後,會呼叫到監聽OnPreparedListener中的onPrepared方法,來進行相應的視訊播放

4.4 設定點選播放下一節視訊的方法

btn_next.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				if(currentPosition<(videoList.size()-1)){
				currentPosition ++;
				playVideo();
			}
			}
				
			}
		});
btn_next是播放下一節視訊的觸發按鈕,播放原理邏輯與播放上一節視訊內容是一致的

4.5 為播放視訊設定其他的相監聽

//當視訊播放完成時進行的操作監聽
video_view.setOnCompletionListener(new OnCompletionListener() {
			@Override
			public void onCompletion(MediaPlayer mp) {
			//例如可以更新播放進度的指示大小,以及其他相關的UI操作
				
			}
		});
//視訊緩衝進度的監聽
		video_view.setOnBufferingUpdateListener(new OnBufferingUpdateListener() {
			@Override
			public void onBufferingUpdate(MediaPlayer mp, int percent) {
				//percent:0-100
				long bufferProgress = (long) ((video_view.getDuration()/100f)*percent);
				//這裡設定顯示視訊緩衝進度條的顯示   video_seekbar 是指示視訊播放的進度條
				video_seekbar.setSecondaryProgress((int) bufferProgress);
			}
		});
//視訊播放卡頓時進行的操作
		video_view.setOnInfoListener(new OnInfoListener() {
			@Override
			public boolean onInfo(MediaPlayer mp, int what, int extra) {
				switch (what) {
				case MediaPlayer.MEDIA_INFO_BUFFERING_START :
				//開始卡頓  顯示緩衝載入的進度條
					ll_buffer.setVisibility(View.VISIBLE);
					break;
				case MediaPlayer.MEDIA_INFO_BUFFERING_END :
				//卡頓結束  隱藏緩衝載入的進度條
					ll_buffer.setVisibility(View.GONE);
					break;
				}
				return true;
			}
		});
//視訊播放錯誤的監聽
		video_view.setOnErrorListener(new OnErrorListener() {
			@Override
			public boolean onError(MediaPlayer mp, int what, int extra) {
				switch (what) {
				case MediaPlayer.MEDIA_ERROR_UNKNOWN :
					//未知格式,視訊檔案錯誤,進行相關資訊的提示
					Toast.makeText(VitamioPlayerActivity.this, "視訊格式不支援", 0).show();
	
					break;
				}
				return true;
			}
		});
	}

4.6 對播放中指示播放進度的進度條(video_seekbar)的操作

4.6.1 指示快取進度的操作

在setOnBufferingUpdateListener這個監聽中操作了video_seekbar的快取進行更新

4.6.2 實時更新播放的指示進度

private void updatePlayProgress(){
		//顯示當前的播放進度
		tv_current_position.setText(StringUtil.formatVideoDuration(video_view.getCurrentPosition()));
		//設定進度條的指示位置
		video_seekbar.setProgress((int) video_view.getCurrentPosition());
		handler.sendEmptyMessageDelayed(1, 500);
	}
	private Handler handler = new Handler(){
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case 1:
				updatePlayProgress();
				break;
			
		};
	};

可以看到,這裡使用的是handler訊息機制,迴圈傳送訊息

4.6.3 手動拖動進度條控制視訊播放的進度

video_seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {
				
			}
			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {
				
			}
			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
					boolean fromUser) {
				if(fromUser){
					//fromUser =true;表示是使用者手動拖動
					//播放相應位置的視訊
					video_view.seekTo(progress);
					//更新播放時間進度顯示
					tv_current_position.setText(StringUtil.formatVideoDuration(progress));
				}
			}
		});


4.7 對播放中指示播放音量的進度條操作

4.7.1 初始化顯示系統的音量

private AudioManager audioManager;
private int currentVolume;//系統音樂和視訊型別當前的音量
private boolean isMute = false;//是否是靜音模式
private int maxVolume;//系統中音樂和視訊型別最大音量
private SeekBar voice_seekbar;//顯示音量指示資訊的進度條

private void initVolume(){
		audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
		//maxVolume:0-15   
		maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
		currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
		//設定音量指示進度條的最大指示進度
		voice_seekbar.setMax(maxVolume);
		//設定當前的音量大小對應的指示進度顯示
		voice_seekbar.setProgress(currentVolume);
	}

voice_seedbar 是視訊頁面指示播放音量大小的進度條

4.7.2 手動拖動進度條改變播放音量的大小

與拖動指示播放進度的進度條的方法一致

voice_seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {
				
			}
			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {
				
			}
			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
					boolean fromUser) {
				if(fromUser){//表示是使用者手動拖動
					isMute = false;
					//記錄當前的音量
					currentVolume = progress;
					//更新系統音量的操作
					updateSystemVolume();
				}
			}
		});

       /**
	 * 更新系統音量
	 */
	private void updateSystemVolume(){
		if(isMute){
		//靜音模式,設定音量大小為0,並將指示音量大小的進度條進度設定為0
			audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);
			voice_seekbar.setProgress(0);
		}else {
		//設定當前改變到的音量大小的進度
			voice_seekbar.setProgress(currentVolume);
			//第三個引數如果是1,會顯示音量改變的浮動面板
		audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0);
		}
	}


4.7.3 手動上下滑動螢幕改變播放音量的大小

涉及到手勢操作,做一些初始化操作準備

int  touchSlop = ViewConfiguration.getTouchSlop();

GestureDetector gestureDetector = new GestureDetector(this,new MyOnGestureListner());

可以在相關方法中做一些滑動過程中的操作

private class MyOnGestureListner extends SimpleOnGestureListener{
		@Override
		public void onLongPress(MotionEvent e) {
			super.onLongPress(e);
			
		}

		@Override
		public boolean onDoubleTap(MotionEvent e) {
			
			return super.onDoubleTap(e);
		}

		@Override
		public boolean onSingleTapConfirmed(MotionEvent e) {
			
			return super.onSingleTapConfirmed(e);
		}
		
	}

捕捉觸控事件,並將事件傳遞給gestureDetector;
private float downY;
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		gestureDetector.onTouchEvent(event);
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			downY = event.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			float moveY = event.getY();
			float moveDistance = moveY - downY;
			
			//對滑動距離進行一個值的限制
			if(Math.abs(moveDistance)<touchSlop)break;
			
			isMute = false;
			
			float totalDistance = Math.min(screenHeight, screenWidth);
			float movePercent = Math.abs(moveDistance)/totalDistance;
			
			int moveVolume = (int) (movePercent*maxVolume);//這個值一定是0
			
			if(moveDistance>0){
				//減小音量
				currentVolume -= 1;
			}else {
				//增大音量
				currentVolume += 1;
			}
			updateSystemVolume();
			
			downY = moveY;
			break;
		case MotionEvent.ACTION_UP:
			
			break;
		}
		return super.onTouchEvent(event);
	}


相關推薦

Android多媒體視訊播放高階開發

1.獲取播放的資料來源 播放視訊的資料來源一般有兩個,一個是請求網路,從伺服器後臺直接獲取播放的視訊資訊,另一種是播放手機中本地的視訊,這裡我們採用的播放源為播放手機本地的視訊 1.1 查詢獲取手機中的視訊的資訊 1.1.1 查詢方法一 定義要查詢到的視訊的資訊,包括視

Android視訊播放的使用及解決的問題

前言 根據專案要求 做一個淘寶今日頭條的功能 要求實現圖文和視訊的混排 在之前的部落格中我已經寫了頭條滾動demo———今日頭條的demo地址 就是這樣的一個效果 點選這個控制元件後 進入↓↓↓頭條的頁面 這篇部落格主要是記錄視訊的使用 關於文

Android自定義視訊播放(網路/本地)

最近因公司專案要求需要寫一個播放器,自帶的又不太好用,也不太好看。自能自定義啦。查看了很多資料,都沒有完善的,還好得以為前輩的指點得以完成,感謝Yang。本篇裡面我有可能有些地方寫得不好(都附上了註釋)。希望各路大神指點,虛心受教。 先來個圖(原始碼在後面附上) 視訊列表裡面

Android SurfaceView】視訊播放 簡單例子

找過好過視訊播放器的例子,但是都特麼給一半程式碼,不給一半程式碼! 還有的 我也是醉了!把自己以前的筆記發一下吧, 雖然有缺陷: 1,橫屏切換的時候,執行緒會死掉,不能啟用...  (已經搞定了!謝謝各位dalao!) 2,停止的時候,執行緒釋放了資源,但是還是有一張圖片

Android自定義視訊播放(三)

一、引言 在上文中,我們通過自定義控制面板的佈局,來實現自定義的播放控制,下面來對裡面的各個元件進行事件繫結。 @Override public void onClick(View v) { if ( v == btnVoice ) {

android 顯示flash視訊播放

因專案需要在web view中能開啟網頁連結,但是有的網頁中包含了使用了flash的視訊播放器,如果是預設的web view 開啟這樣的網頁flash的位置為顯示為空白,根據網上查詢的資料通過以下方式可以解決(android 4.4): webSettings

Android筆記:視訊播放播放本地視訊和網路視訊

這篇博文主要是記錄一下VideoView的使用,這個demo使用VideoView來播放本地視訊和網路視訊。 先來看一下效果圖: 接下來說程式碼: 1。佈局檔案: <?xml ve

Android自定義視訊播放(六)

一、引言 現在已經差不多完成了一個自定義的視訊播放器,還有一個讓視訊全屏播放的按鈕事件沒有繫結,下面會介紹。此外還要一個非常重要的功能就是,已經寫好了自定義的播放器,那麼就應該能被其他應用調起,來播放視訊,下面來完善這兩個功能。 二、全屏按鈕事件

Android自定義視訊播放(一)

一、引言 我們在開發Android多媒體應用時,有兩種方式來播放多媒體資源。第一種是使用隱式的Intent,來使用系統或者手機已經安裝的第三方播放器應用來播放音視訊,第二種是使用Android自帶的、我們自定義的播放器來播放,這種主要是採用Android提供的

Android系列音樂播放的實現一(播放手機記憶體音訊檔案)

現在給大家實現的是利用手機記憶體卡里的音訊檔案去播放的,至於xml佈局檔案大家可以去看我上一篇部落格的,因為這兩篇部落格是相關聯的。現在就開始吧!xml配置檔案許可權:(一定要加上) <uses-permission android:name="android.per

Android自定義視訊播放(二)

一、引言 上一篇在對VideoView使用時,加上了這樣一行程式碼: videoView.setMediaController(new MediaController(this)); 這行程式碼為VideoView加上了控制面板,可以操作視訊播放的快

基於NDK、C++、FFmpeg的android視訊播放開發實戰-夏曹俊-專題視訊課程

基於NDK、C++、FFmpeg的android視訊播放器開發實戰—1796人已學習 課程介紹         課程包含了對流媒體(拉流)的播放,演示了播放rtmp的香港衛視,支援rtsp攝像頭和h

Android簡單視訊播放VideoView(一)

早上起來有時間,發一篇博文,最近在開發電視機頂盒的視訊播放,涉及到Android當中比較常見的視訊播放器控制元件的使用,以此為例,記錄下來。 首先,上效果圖: 通過VideoView播放視訊的步驟: 實現方式:使用XML佈局和java程式碼控制元

三、VR視訊播放開發 ---- Android VR視訊/Google VR for Android /VR Pano/VR Video

simplevideowidget 如果沒有看上一篇文章的請先看完再來看這一篇吧,有寫重複的就不介紹了 AndroidManifest 上一篇文章有提到,其實這裡也沒有什麼特別的 build.gradle dependencie

Android使用VLC庫開發自己的視訊播放

clone完成之後,把工程目錄下的java_sample/src/main/java/org/videolan/javasample/JavaActivity.java拷貝至TestVideoPlayer工程videoPlayer目錄下,再新增必要的檔案之後,整個工程目錄如下:

安卓多媒體播放開發

lis list 媒體 ongl .com ava aid 多媒體 http JaVa%E5%8F%8D%E5%B0%84%E6%9C%BA%E5%88%B6%E5%AE%9E%E7%8E%B0%E5%8F%8A%E5%8E%9F%E7%90%86 http://musi

android,Exoplayer實現視訊播放

bundle配置: implementation 'com.google.android.exoplayer:exoplayer-core:2.8.1'implementation 'com.google.android.exoplayer:exoplayer-dash:2.8.1'implementati

關於使用OpenCV-python開發簡易視訊播放

正在研究開簡易如何開發簡易視訊播放器,找了一些一列,包括在pyglet上面的程式碼,但是好長,執行出錯。 看到一個很簡潔的程式碼,沒有報錯但是彈開之後不會自動播放視訊,也沒有生成應用程式。 http://blog.51cto.com/7335580/2145914 這是他的連結,很簡潔

android形狀屬性、鎖屏密碼、動態模糊、kotlin專案、抖音動畫、記賬app、視訊播放等原始碼

Android精選原始碼 直觀瞭解Android的“形狀”屬性如何影響Drawable的外觀。 一個靈活的視訊播放器, 可替換播放器核心。 android鎖屏輸入密碼功能原始碼 背景動態模糊方案,元件實現類對bitmap模糊處理的各類算... 簡單天氣,帶

android平臺下基於ffmpeg和ANativeWindow實現簡單的視訊播放

音視訊實踐學習 android全平臺編譯ffmpeg以及x264與fdk-aac實踐 ubuntu下使用nginx和nginx-rtmp-module配置直播推流伺服器 android全平臺編譯ffmpeg合併為單個庫實踐 android-studio使用c