編輯:關於Android編程
多媒體(duō méi tǐ) 的英文單詞是Multimedia,它由media和multi兩部分組成。一般理解為多種媒體的綜合
多媒體是計算機和視頻技術的結合,實際上它是兩個媒體;聲音和圖像,或者用現在的術語:音響和電視。
多媒體(Multimedia),在計算機系統中,組合兩種或兩種以上媒體的一種人機交互式信息交流和傳播媒體
使用的媒體包括文字、圖片、照片、聲音 (包含音樂、語音旁白、特殊音效)、動畫和影片,以及程式所提供的互動功能
多媒體是超媒體(Hypermedia)系統中的一個子集,而超媒體系統是使用超鏈接 (Hyperlink)構成的全球信息系統
全球信息系統是因特網上使用 TCP/IP 協議和 UDP/IP 協議
Android 官方提供了MediaPlayer 核心類,用於播放音樂,其狀態流程如下圖所示。MediaPlayer 必須嚴格按照狀態圖操作,否則就會出現錯誤,這些錯誤都是底層拋出,嚴格按照狀態圖操作的話一般就不會出問題。
MediaPlayer,原生的API,可以播放音視頻,但是支持的格式比較少,實際開發中用的比較少,但是還是很有必要學習,熟悉API,因為Vitamio框架的API大部分跟原生的API是一樣的
為了演示MediaPlayer 的使用,我們需要提前准備一個mp3 文件放到sdcard 中
需求:制作一個播放器,能夠播放/暫停/停止音樂文件,並且添加一個SeekBar(可以拖拽的ProgressBar),當音樂播放時SeekBar 也會不斷的跟新當前的進度,當用戶拖動SeekBar 時可以更改播放的進度
布局文件<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
代碼實現
public class MainActivity extends Activity implements OnSeekBarChangeListener { private SeekBar sb; private MediaPlayer player; private int duration; // 播放器的幾個狀態 private static final int PLAYING = 1;// 播放狀態 private static final int PAUSING = 2;// 暫停狀態 private static final int STOPPING = 3;// 停止狀態 private volatile int CURRENT = 0;// 當前狀態 private Timer timer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sb = (SeekBar) findViewById(R.id.sb); //設置拖動監聽 sb.setOnSeekBarChangeListener(this); } //播放 public void play(View view) { if (player != null) { if (CURRENT == PLAYING) { Toast.makeText(this, "音樂已經在播放了", Toast.LENGTH_SHORT).show(); return; } else if (CURRENT == PAUSING) { player.start(); CURRENT = PLAYING; return; } } try { //創建一個播放器對象 player = new MediaPlayer(); //給播放器設置音樂路徑 player.setDataSource("/mnt/sdcard/test.mp3"); //設置音樂格式 player.setAudioStreamType(AudioManager.STREAM_MUSIC); //准備 player.prepare(); //獲取音樂最大長度(毫秒單位) duration = player.getDuration(); //給SeekBar 設置最大值 sb.setMax(duration); //音樂開始播放 player.start(); //設置當前的狀態為播放 CURRENT = PLAYING; if (timer == null) { //創建定時器 timer = new Timer(); } /** * 參數1:匿名內部類,相當於Runnable 類 * 參數2:第一次延時多長時間(毫秒)後執行,0 則代表立即執行 * 參數3:每隔多長時間(毫秒)執行一次 */ timer.schedule(new TimerTask() { @Override public void run() {//該方法每1 秒被調用一次 if (CURRENT == PLAYING) { runOnUiThread(new Runnable() { @Override public void run() { //雙重判斷,盡可能避免線程問題,因為該段代碼時在主線程中的, //第一次判斷是在子線程中進行的 if (player != null && CURRENT == PLAYING) { //獲取當前的播放進度 int currentPosition = player.getCurrentPosition(); //設置給SeekBar sb.setProgress(currentPosition); } } }); } } }, 0, 1000); } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "音樂播放失敗" + e, 0).show(); } } /** * 暫停 */ public void pause(View view) { if (player != null && CURRENT == PLAYING) { player.pause(); CURRENT = PAUSING; } } /** * 停止 */ public void stop(View view) { if (player != null) { if (CURRENT == PLAYING || CURRENT == PAUSING) { CURRENT = STOPPING; //取消定時器 timer.cancel(); timer = null; player.stop(); player.reset(); player.release(); player = null; sb.setProgress(0); } } } /* * 拖動過程中回調多次 */ @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (player == null) { sb.setProgress(0); } else { player.seekTo(progress); } } /* * 開始拖動前回調一次 */ @Override public void onStartTrackingTouch(SeekBar seekBar) { if (player == null) { Toast.makeText(this, "音樂播放器還未開始", Toast.LENGTH_SHORT).show(); } } /* * 結束拖動後回調一次 */ @Override public void onStopTrackingTouch(SeekBar seekBar) { } @Override protected void onDestroy() { super.onDestroy(); stop(null); } }
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1); mediaPlayer.start(); // no need to call prepare(); create() does that for you
Uri myUri = ....; // initialize Uri here MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(getApplicationContext(), myUri); mediaPlayer.prepare(); mediaPlayer.start();
String url = "http://........"; // your URL here MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(url); mediaPlayer.prepare(); // might take long! (for buffering, etc) mediaPlayer.start();
String url = "http://........"; // your URL here MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(url); mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mp.start(); } });
public class MyService extends Service implements MediaPlayer.OnPreparedListener { MediaPlayer mMediaPlayer; private static final String ACTION_PLAY = "com.example.action.PLAY"; MediaPlayer mMediaPlayer = null; public int onStartCommand(Intent intent, int flags, int startId) { ... if (intent.getAction().equals(ACTION_PLAY)) { mMediaPlayer = ... // initialize it here mMediaPlayer.setOnPreparedListener(this); mMediaPlayer.prepareAsync(); // prepare async to not block main thread } } /** Called when MediaPlayer is ready */ public void onPrepared(MediaPlayer player) { player.start(); } } public void initMediaPlayer() { // ...initialize the MediaPlayer here... mMediaPlayer.setOnErrorListener(this); } @Override public boolean onError(MediaPlayer mp, int what, int extra) { // ... react appropriately ... // The MediaPlayer has moved to the Error state, must be reset! }
mMediaPlayer = new MediaPlayer(); // ... other initialization here ... mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); //wifi鎖 WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock"); wifiLock.acquire(); //當暫停,不再需要網絡時釋放鎖 wifiLock.release();
String songName; // assign the song name to songName PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getApplicationContext(), MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); Notification notification = new Notification(); notification.tickerText = text; notification.icon = R.drawable.play0; notification.flags |= Notification.FLAG_ONGOING_EVENT; notification.setLatestEventInfo(getApplicationContext(), "MusicPlayerSample", "Playing: " + songName, pi); startForeground(NOTIFICATION_ID, notification);
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // could not get audio focus. }
requestAudioFocus() 方法的第一個參數是一個 AudioManager.OnAudioFocusChangeListener,當任何時候音頻焦點發生變化的時候,會回調 OnAudioFocusChangeListener的onAudioFocusChange()方法
class MyService extends Service implements AudioManager.OnAudioFocusChangeListener { // .... public void onAudioFocusChange(int focusChange) { // Do something based on focus change... } }
public class MyService extends Service { MediaPlayer mMediaPlayer; // ... @Override public void onDestroy() { if (mMediaPlayer != null) mMediaPlayer.release(); } }
一般是位於一個mp3文件的開頭或末尾的若干字節內,附加了關於該mp3的歌手,標題,專輯名稱,年代,風格等信息,該信息就被稱為ID3信息,ID3信息分為兩個版本,v1和v2版。 其中:v1版的ID3在mp3文件的末尾128字節,以TAG三個字符開頭,後面跟上歌曲信息。 v2版一般位於mp3的開頭,可以存儲歌詞,該專輯的圖片等大容量的信息。
在該節中,視頻播放依然使用MediaPlayer 類,為了方便演示,我們直接使用本文中創建的工程,只需在布局文件添加SurfaceView 控件即可
布局文件
實現代碼
public class MainActivity extends Activity { private MediaPlayer player; static int currentPosition; private SurfaceView sv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sv = (SurfaceView) findViewById(R.id.sv); //拿到surfaceview的控制器 final SurfaceHolder sh = sv.getHolder(); // Thread t = new Thread(){ // @Override // public void run() { // try { // sleep(200); // } catch (InterruptedException e) { // e.printStackTrace(); // } // runOnUiThread(new Runnable() { // @Override // public void run() { // MediaPlayer player = new MediaPlayer(); // player.reset(); // try { // player.setDataSource("sdcard/2.3gp"); // player.setDisplay(sh); // player.prepare(); // player.start(); // } catch (Exception e) { // e.printStackTrace(); // } // // } // }); // // } // }; // t.start(); sh.addCallback(new Callback() { //surfaceView銷毀時調用 @Override public void surfaceDestroyed(SurfaceHolder holder) { //每次surfaceview銷毀時,同時停止播放視頻 if(player != null){ currentPosition = player.getCurrentPosition(); player.stop(); player.release(); player = null; } } //surfaceView創建時調用 @Override public void surfaceCreated(SurfaceHolder holder) { //每次surfaceView創建時才去播放視頻 if(player == null){ player = new MediaPlayer(); player.reset(); try { player.setDataSource("sdcard/2.3gp"); player.setDisplay(sh); player.prepare(); player.start(); player.seekTo(currentPosition); } catch (Exception e) { e.printStackTrace(); } } } //surfaceView結構改變時調用 @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } }); } }
VideoView 跟MediaPlayer 相比播放視頻步驟要簡單的多,因為VideoView 原生提供了播放,暫停、快進、快退、進度條等方法。使用起來要方便的很多
1、設置布局文件,布局文件比較簡單,因此這裡只給你VideoView 標簽
2、設置VideoView 的播放文件路徑和媒體控制器,調用start 方法即可播放媒體文件
//實例化VideoView 對象 vv = (VideoView) findViewById(R.id.vv); //從界面獲取播放路徑 et_path = (EditText) findViewById(R.id.et_path); //給VideoView 設置視頻路徑 vv.setVideoPath(et_path.getText().toString()); //設置VideoView 控制器,我們當前類實現了MediaPlayerControl 接口 vv.setMediaController(new MediaController(this)); //開始播放 vv.start(); //設置當前播放器窗口設置為焦點 vv.requestFocus();
3、覆寫MediaPlayerControl 接口中的抽象方法
@Override public void start() { } @Override public void pause() { } @Override public int getDuration() { return 0; } @Override public int getCurrentPosition() { return 0; } @Override public void seekTo(int pos) { } @Override public boolean isPlaying() { return false; } @Override public int getBufferPercentage() { return 0; } @Override public boolean canPause() { return false; } @Override public boolean canSeekBackward() { return false; } @Override public boolean canSeekForward() { return false; } @Override public int getAudioSessionId() { return 0; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return super.onKeyDown(keyCode, event); } @Override public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { return super.onKeyMultiple(keyCode, repeatCount, event); }
注意:上面的方法都是回調方法,我們可以在這些方法裡面實現我們的業務邏輯。只有當我們給VideoView設置setMediaController 後控制器才會出現
電影文件有很多基本的組成部分。首先,文件本身被稱為容器Container,容器的類型決定了信息被存放在文件中的位置。AVI和Quicktime就是容器的例子。接著,你有一組流,例如,你經常有的是一個音頻流和一個視頻流。(一個流只是一種想像出來的詞語,用來表示一連串的通過時間來串連的數據元素)。在流中的數據元素被稱為幀Frame。每個流是由不同的編碼器來編碼生成的。編解碼器描述了實際的數據是如何被編碼Coded和解碼DECoded的,因此它的名字叫做CODEC。接著從流中被讀出來的叫做包Packets。包是一段數據,它包含了一段可以被解碼成方便我們最後在應用程序中操作的原始幀的數據。
七個模塊分別為:讀文件模塊,解復用模塊 ,視頻解碼模塊,音頻解碼音頻,顏色空間轉換模塊,視頻顯示模塊,音頻播放模塊
粗略的分為五類,分別是 Source filer, Demux flter, Decoder filter, Color Space converter filter,Render filter,各類 filter的功能與作用簡述如下
Source filter 源過濾器的作用是為下級 demux filter 以包的形式源源不斷的提供數據流。在通常情況下,我們有多種方式可以獲得數據流,一種是從本地文件中讀取,一種是從網上獲取,Sourcefilter 另外一個作用就是屏蔽讀本地文件和獲取網絡數據的差別,在下一級的 demux filter 看來,本地文件和網絡數據是一樣的。
解復用過濾器的作用是識別文件類型,媒體類型,分離出各媒體原始數據流,打上時鐘信息後送給下級 decoder filter。為識別出不同的文件類型和媒體類型,常規的做法是讀取一部分數據,然後遍歷解復用過濾器支持的文件格式和媒體數據格式,做匹配來確定是哪種文件類型,哪種媒體類型;有些媒體類型的原始數據外面還有其他的信息,比如時間,包大小,是否完整包等等。demux filter 解析數據包後取出原始數據,有些類型的媒體不管是否是完整包都立即送往下級 decoder filter,有些類型的媒體要送完整數據包,此時可能有一些數據包拼接的動作;當然時鐘信息的計算也是 demux filter 的工作內容,這個時鐘用於各媒體之間的同步。在本例中,AVI Splitter 是 Demux filter。
解碼過濾器的作用就是解碼數據包,並且把同步時鐘信息傳遞下去。對視頻媒體而言,通常是解碼成 YUV 數據,然後利用顯卡硬件直接支持 YUV 格式數據 Overlay 快速顯示的特性讓顯卡極速顯示。YUV格式是一個統稱,常見的有 YV12,YUY2,UYVY 等等。有些非常古老的顯卡和嵌入式系統不支持 YUV 數據顯示,那就要轉換成 RGB 格式的數據,每一幀的每一個像素點都要轉換,分別計算 RGB 分量,並且因為轉換是浮點運算,雖然有定點算法,還是要耗掉相當一部分 CPU,總體上效率底下;對音頻媒體而言,通常是解碼成 PCM 數據,然後送給聲卡直接輸出。在本例中,AVI Decompress 和 ACM Warper 是 decoder filter。
顏色空間轉換過濾器的作用是把視頻解碼器解碼出來的數據轉換成當前顯示系統支持的顏色格式。通常視頻解碼器解碼出來的是 YUV 數據,PC 系統是直接支持 YUV 格式的,也支持 RGB 格式,有些嵌入式系統只支持 RGB 格式的。在本例中,視頻解碼器解碼出來的是 RGB8 格式的數據,Color space converter filter 把 RGB8 轉換成 RGB32 顯示。
渲染過濾器的作用就是在適當的時間渲染相應的媒體,對視頻媒體就是直接顯示圖像,對音頻就是播放聲音。視音頻同步的策略方法有好幾種,其中最簡單的一種就是默認視頻和音頻基准時間相同,這時音頻可以不打時鐘信息,通過計算音頻的采樣頻率,量化 bit 數,聲道數等基本參數就知道音頻 PCM 的數據速率,按照這個速率往前播放即可;視頻必須要使用同步時鐘信息來決定什麼時候顯示。DirectShow 采用一個有序鏈表 ,把接收到的數據包放進有序鏈表中,啟動一個定時器,每次定時器時間到就掃描鏈表,比較時鐘信息,或者顯示相應的幀,或者什麼也不做,每次接收到新的數據幀,首先判斷時鐘信息,如果是歷史數據幀就丟棄,如果是將來顯示數據幀就進有序鏈表,如果當前時間幀就直接顯示。如此這樣,保持視頻和音頻在人體感覺誤差范圍內相對的動態同步。在本例中 VideoRender 和 Default DirectSound Device 是 Render filter,同時也是 Sink filter
JetPlayer jetPlayer = JetPlayer.getJetPlayer(); jetPlayer.loadJetFile("/sdcard/level1.jet"); byte segmentId = 0; // queue segment 5, repeat once, use General MIDI, transpose by -1 octave jetPlayer.queueJetSegment(5, -1, 1, -1, 0, segmentId++); // queue segment 2 jetPlayer.queueJetSegment(2, -1, 0, 0, 0, segmentId++); jetPlayer.play();
可能我們在開發中會時常用到計時器這玩意兒,比如在錄像的時候,我們可能需要在右上角顯示一個計時器。這個東西其實實現起來非常簡單。只需要用一個控件Chronometer,是的
一.WorkSpace是什麼前面已經介紹了一個WorkSpace包含了多個CellLayout,再回憶下之前畫過的圖WorkSpace是一個ViewGroup,它的布局如
本文實例講述了Android編程實現手繪及保存為圖片的方法。分享給大家供大家參考,具體如下:運行效果圖預覽:應 yzuo_08 要求做了此Demo,跟以前那個手寫板Dem
字體管家是最好的字體下載工具,提供字體下載、字體備份、字體預覽、字體修復功能,永久免費的好軟件,為您提供最好用的字體下載字體備份字體安裝軟件。我們來看看如何