【Android 多媒体开发】 MediaPlayer 网络视频播放器
作者 : 万境绝尘 (octopus_truth@163.com)
转载请著名出处 :http://blog.csdn.net/shulianghan/article/details/38895143
一. 相关模块解析
1. 播放载体 SurfaceView 简介
(1) SurfaceView 与 Surface
SurfaceView 与 Surface 简介 : SurfaceView 中嵌入了一个 Surface, SurfaceView 可以操控 Surface 的 位置, 大小尺寸等;
-- SurfaceView 绘制图像可见性 : Surface 相当与一块画板, 上面绘制了一些东西, 但是不是所有的内容都可见, SurfaceView 相当与 Surface 的窗口, 只有在 SurfaceView 窗口区域内的才是可见的, 其它没有在窗口正下方的内容不可见;
-- Surface 与 SurfaceView 声明周期对应 : SurfaceView 可见的时候 Surface 被创建, SurfaceView 不可见时 Surface 销毁, Surface 创建销毁时机回调方法 SurfaceCreated(SurfaceHolder) 和 SurfaceDestoryed(SurfaceHolder) 方法, 这两个回调方法是 SurfaceHolder.Callback 中设定;
-- Surface 控制 : SurfaceView 通过 SurfaceHolder 控制 Surface;
(2) SurfaceHolder 简介
Surfaceholder 简介 : SurfaceHolder 是 Surface 的控制器, 用于控制 SurfaceView 绘图, 处理画布上的 动画 渲染效果 尺寸等;
-- 添加 Callback 回调接口 :abstract void addCallback(SurfaceHolder.Callback callback), 添加一个 SurfaceHolder.Callback 接口对象, 监听 Surface 的开始结束绘制大小改变事件;
-- 锁定画布 : abstract Canvas lockCanvas(), 锁定画布, 可以获得 Canvas 对象, 之后就可以在 Canvas 上绘图了;
(3) SurfaceHolder.Callback 简介
SurfaceHolder.Callback 接口 :
-- Surface 绘图边界 : 所有的绘图操作, 在 Surface 创建后操作, 在 Surface 销毁之前结束;
-- SurfaceHolder.Callback 接口对应 Surface 边界 : SurfaceCreated() 方法在 Surface 创建时回调, SurfaceDestroyed() 方法在 Surface 销毁前回调;
SurfaceHolder.Callback 接口中的方法简介 :
-- SurfaceChanged() : Surface 大小改变前回调;
-- surfaceCreated() : surface 创建时回调;
-- surfaceDestroyed() : surface 销毁时回调;
2. SurfaceView 使用流程
(1) 在布局文件中使用 SurfaceView 组件
布局文件 : 在布局文件中使用 <SurfaceView /> 组件;
-- 示例 :
<SurfaceView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="3"/>
(2) 设置 SurfaceHolder.Callback 回调接口
创建回调接口子类 : Activity 继承SurfaceHolder.Callback 接口, 实现接口的三个方法 SurfaceCreated(), SurfaceChanged(), SurfaceDestroyed() 方法;
-- 示例 : 一般情况下直接设置 Activity 实现该接口, 为 SurfaceHolder 设置接口的时候直接将 Activity 设置进去;
public class HomeActivity extends Activity implements SurfaceHolder.Callback { /** * 在 Surface 大小发生改变的时候回调 * 实现的 SurfaceHolder.Callback 接口方法 */ @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { System.out.println("SurfaceHolder.Callback.surfaceChanged : Surface 大小发生改变"); } /** * 在 Surface 创建的时候回调, 一般在该方法中开始绘图 * 实现的 SurfaceHolder.Callback 接口方法 */ @Override public void surfaceCreated(SurfaceHolder arg0) { System.out.println("SurfaceHolder.Callback.surfaceCreated : Surface 开始创建"); } /** * 在 Surface 销毁之前回调, 在该方法中停止渲染线程, 释放相关资源 * 实现的 SurfaceHolder.Callback 接口方法 */ @Override public void surfaceDestroyed(SurfaceHolder arg0) { System.out.println("SurfaceHolder.Callback.surfaceDestroyed : Surface 销毁"); } }
(3) 获取 SurfaceHolder 对象 并添加回调接口子类
获取 SurfaceHolder 流程 :
-- a. 获取 SurfaceView : 使用 findViewById() 方法从布局文件中获取 SurfaceView 组件对象;
surface_view = (SurfaceView) findViewById(R.id.surface_view);-- b. 从 SurfaceView 组件对象中获取 SurfaceHolder : 调用 SurfaceView 对象的 getHolder() 即可获取 SurfaceHolder 对象;
surface_holder = surface_view.getHolder(); /* 根据 SurfaceView 组件, 获取 SurfaceHolder 对象 */-- c. 为 SurfaceHolder 设置回调接口 : 调用 Surfaceholder 的 setCallback() 方法即可, 用于监听 Surface 的创建 改变 和 销毁;
surface_holder.addCallback(this); /* 为 SurfaceHolder 设置回调函数, 即 SurfaceHolder.Callback 子类对象 */
SurfaceHolder 其它设置 :
-- 设置 SurfaceHolder 画面比例 : 调用 SurfaceHolder 的setFixedSize() 方法;
surface_holder.setFixedSize(160, 128); /* 设置视频大小比例 */-- 设置 SurfaceHolder 数据类型 : 调用 SurfaceHolder 的setType() 方法;
surface_holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);/* 设置视频类型 */
总体代码示例 :
/** * 初始化相关数据变量 */ private void initData() { /* 获取并设置 SurfaceHolder 对象 */ surface_holder = surface_view.getHolder(); /* 根据 SurfaceView 组件, 获取 SurfaceHolder 对象 */ surface_holder.addCallback(this); /* 为 SurfaceHolder 设置回调函数, 即 SurfaceHolder.Callback 子类对象 */ surface_holder.setFixedSize(160, 128); /* 设置视频大小比例 */ surface_holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);/* 设置视频类型 */ }
3.AutoCompleteTextView 组件
组件特性 : 可以设置一个适配器, 适配器中维护一组字符串, 当输入一部份字符后, 能自动将符合条件的提示出来;
-- 示例 :
(1) 在布局文件中定义组件
布局文件中 : 使用 <AutoCompleteTextView /> 标签添加组件;
-- 设置后面省略 :android:ellipsize="end" , 由于链接太长, 不能显示全部, 自动将后面的部分省略;
-- 设置提示 :android:completionHint="选择下载的视频地址", 设置弹出的提示栏最后一栏的提示;
-- 设置单行显示 :android:singleLine="true";
-- 设置输入几个字符之后开始提示 :android:completionThreshold="1", 这里设置的是输入第一个字符之后就开始自动提示;
-- 示例 :
<AutoCompleteTextView android:id="@+id/url" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:ellipsize="end" android:completionThreshold="1" android:text="http://daily3gp.com/vids/747.3gp" android:completionHint="选择下载的视频地址"/>
(2) 创建适配器
创建适配器方法 :
android.widget.ArrayAdapter.ArrayAdapter<String>(Context context, int textViewResourceId, String[] objects)-- Context context 参数 : 上下文对象;
--int textViewResourceId 参数 : 显示列表的组件的布局;
--String[] objects 参数 : 字符串数组对象;
代码示例 :
/* 设置一个列表适配器 */ String[] urls = { "http://daily3gp.com/vids/747.3gp", "http://daily3gp.com/vids/Funny%20women%20cannot%20understand.3gp", "http://k.youku.com/player/getFlvPath/sid/9409280845322127f6c57_00/st/flv/fileid/0300020100540024BC9E5C08BD8A98D8200E2B-7950-B9A5-8669-DC283BDCC077?K=3a58dc2cdcc532df261dddec&ctype=12&ev=1&oip=1931322792&token=5696&ep=eyaUE0uFVsYE4CDdij8bYHrkJ3IIXP4J9h%2BFg9JjALshTOi%2FmzqjtJTFS4xCHottelMPGJ%2F5qdDnH0JmYfdKrGgQrUfZPPro%2BPbq5dkgxpgDFG1FAc3Qs1SbRTn3", "http://k.youku.com/player/getFlvPath/sid/9409280845322127f6c57_00/st/flv/fileid/030002040053FFB59E433100422C39BAFA46CC-4DED-E928-87B8-91706CDB5FF2?K=645d8478a3aa59052411eb8a&ctype=12&ev=1&oip=1931322792&token=5696&ep=eyaUE0uFVsYE4CDdij8bYHrkJ3IIXP4J9h%2BFg9JmALshS57J6zvYspmzTf5CFv0bcFEFGZmA3aHjbDNnYfQ33BwQqkeqMfro%2BYLr5aRSw5AGFW1Ed7uhtlSbRTn3" }; /* 创建数组适配器 */ ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, urls); /* 将适配器设置给 AutoCompleteTextView 组件对象 */ url.setAdapter(adapter);
4. MediaPlayer 播放
(1) 设置音量 和 播放载体
设置音量 :
mediaPlayer.setAudioStreamType(2); /* 设置播放音量 */
设置播放载体 : 调用 setDisplay() 方法, 传入 SurfaceHolder 对象;
mediaPlayer.setDisplay(surface_holder); /* 设置播放载体 */
(2) 设置各种监听器
设置错误监听器 : 如果出现错误, 会回调该监听器中的方法, 并提供错误码;
/* 设置 MediaPlayer 错误监听器, 如果出现错误就会回调该方法打印错误代码 */ mediaPlayer.setOnErrorListener(new OnErrorListener() { @Override public boolean onError(MediaPlayer arg0, int what, int extra) { System.out.println("MediaPlayer 出现错误 what : " + what + " , extra : " + extra); return false; } });
设置缓冲进度监听器 : 缓冲有进展后, 回调该监听器中的方法, 传入缓冲的数据百分比;
/* 设置缓冲进度更新监听器 */ mediaPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener() { @Override public void onBufferingUpdate(MediaPlayer arg0, int percent) { /* 打印缓冲的百分比, 如果缓冲 */ System.out.println("缓冲了的百分比 : " + percent + " %"); } });
设置播放完毕监听器 : 播放完毕后会回调该监听器中的方法;
/* 设置播放完毕监听器 */ mediaPlayer.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer arg0) { System.out.println("播放完毕了"); status.setText("播放完毕"); } });
设置准备完毕回调监听器 : 准备完毕后会回调该方法;
/* 设置准备完毕监听器 */ mediaPlayer.setOnPreparedListener(new OnPreparedListener() { @Override public void onPrepared(MediaPlayer arg0) { System.out.println("准备完毕"); /* 设置播放状态 */ status.setText("播放中"); } });
二. 代码示例
1. 布局文件代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="20dp" tools:context="cn.org.octopus.videodemo.HomeActivity" tools:ignore="MergeRootFrame" android:orientation="vertical"> <AutoCompleteTextView android:id="@+id/url" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:ellipsize="end" android:completionThreshold="1" android:text="http://daily3gp.com/vids/747.3gp" android:completionHint="选择下载的视频地址"/> <SurfaceView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="3"/> <TextView android:id="@+id/status" android:layout_width="match_parent" android:gravity="center" android:layout_height="0dp" android:layout_weight="1" android:textSize="20dp" android:text="状态"/> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:gravity="center_vertical" android:orientation="horizontal"> <Button android:id="@+id/play" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:onClick="onClick" android:text="播放"/> <Button android:id="@+id/pause" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:onClick="onClick" android:text="暂停"/> <Button android:id="@+id/reset" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:onClick="onClick" android:text="重放"/> <Button android:id="@+id/stop" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:onClick="onClick" android:text="停止"/> </LinearLayout> </LinearLayout>
2. AndroidManifest 代码
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.org.octopus.videodemo" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="19" /> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="cn.org.octopus.videodemo.HomeActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
3. Activity 代码
package cn.org.octopus.videodemo; import java.io.IOException; import android.R.anim; import android.app.Activity; import android.content.DialogInterface.OnClickListener; import android.graphics.PixelFormat; import android.media.MediaPlayer; import android.media.MediaPlayer.OnBufferingUpdateListener; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; import android.os.Bundle; import android.support.v4.view.ViewPager; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.TextView; /** * * * SurfaceHolder * 1. 简介 : 是 Surface 的控制器, 用于控制 SurfaceView 绘图, 处理画布上的动画, 渲染效果, 大小等; * 2. 常用方法 : * -- abstract void addCallback(SurfaceHolder.Callback callback) : 添加一个 SurfaceHolder.Callback 接口对象, 监听 Surface 的开始结束绘制大小改变事件; * -- abstract Canvas lockCanvas() : 锁定画布, 可以获得 Canvas 对象, 之后就可以在 Canvas 上绘图了; * * SurfaceHolder.Callback接口 : * 1. Surface 绘图边界 : 所有的绘图工作都在 Surface 创建之后才能进行, 在 Surface 销毁之前结束; * 2. Callback 接口对应的 Surface 边界 : surfaceCreated() 方法在开始绘制时回调, surfaceDestroyed() 在 Surface 销毁前回调; * 3. 该接口中的方法 : * -- surfaceChanged() : 在 Surface 大小改变时回调; * -- surfaceCreated() : 在 Surface 创建时回调; * -- surfaceDestroyed() : 在 Surface 销毁时回调; * * @author octopus * */ public class HomeActivity extends Activity implements SurfaceHolder.Callback { private AutoCompleteTextView url; /* 地址输入框, 带自动提示功能 */ private SurfaceView surface_view; /* 播放视频载体 */ private TextView status; /* 显示播放状态 */ private Button play; /* 播放按钮 */ private Button pause; /* 咱提供按钮 */ private Button reset; /* 重放按钮 */ private Button stop; /* 停止按钮 */ private MediaPlayer mediaPlayer; /* 播放器 */ private SurfaceHolder surface_holder; /* Surface 控制器 */ private boolean isStartPlaying; /* 是否开始了播放 */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); initViews(); initData(); } /** * 初始化成员变量中的组件变量 */ private void initViews() { /* 通过 findViewById 获取相关方法 */ url = (AutoCompleteTextView) findViewById(R.id.url); surface_view = (SurfaceView) findViewById(R.id.surface_view); status = (TextView) findViewById(R.id.status); play = (Button) findViewById(R.id.play); pause = (Button) findViewById(R.id.pause); reset = (Button) findViewById(R.id.reset); stop = (Button) findViewById(R.id.stop); /* 设置一个列表适配器 */ String[] urls = { "http://daily3gp.com/vids/747.3gp", "http://daily3gp.com/vids/Funny%20women%20cannot%20understand.3gp", "http://k.youku.com/player/getFlvPath/sid/9409280845322127f6c57_00/st/flv/fileid/0300020100540024BC9E5C08BD8A98D8200E2B-7950-B9A5-8669-DC283BDCC077?K=3a58dc2cdcc532df261dddec&ctype=12&ev=1&oip=1931322792&token=5696&ep=eyaUE0uFVsYE4CDdij8bYHrkJ3IIXP4J9h%2BFg9JjALshTOi%2FmzqjtJTFS4xCHottelMPGJ%2F5qdDnH0JmYfdKrGgQrUfZPPro%2BPbq5dkgxpgDFG1FAc3Qs1SbRTn3", "http://k.youku.com/player/getFlvPath/sid/9409280845322127f6c57_00/st/flv/fileid/030002040053FFB59E433100422C39BAFA46CC-4DED-E928-87B8-91706CDB5FF2?K=645d8478a3aa59052411eb8a&ctype=12&ev=1&oip=1931322792&token=5696&ep=eyaUE0uFVsYE4CDdij8bYHrkJ3IIXP4J9h%2BFg9JmALshS57J6zvYspmzTf5CFv0bcFEFGZmA3aHjbDNnYfQ33BwQqkeqMfro%2BYLr5aRSw5AGFW1Ed7uhtlSbRTn3" }; /* 创建数组适配器 */ ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, urls); /* 将适配器设置给 AutoCompleteTextView 组件对象 */ url.setAdapter(adapter); /* 使窗口支持透明度, 把当前 Activity 窗口设置成透明, 设置了该选项就可以使用 setAlpha 等函数设置窗口透明度 */ getWindow().setFormat(PixelFormat.TRANSPARENT); } /** * 初始化相关数据变量 */ private void initData() { /* 获取并设置 SurfaceHolder 对象 */ surface_holder = surface_view.getHolder(); /* 根据 SurfaceView 组件, 获取 SurfaceHolder 对象 */ surface_holder.addCallback(this); /* 为 SurfaceHolder 设置回调函数, 即 SurfaceHolder.Callback 子类对象 */ surface_holder.setFixedSize(160, 128); /* 设置视频大小比例 */ surface_holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);/* 设置视频类型 */ } /** * 设置点击事件 * @param view */ public void onClick(View view) { int id = view.getId(); switch (id) { case R.id.play: /* 播放视频直接从 AutoCompleteTextView 中获取字符串, 播放该 url 代表的网络视频 */ playVideo(url.getText().toString()); break; case R.id.pause: if(mediaPlayer != null){ mediaPlayer.pause(); status.setText("暂停"); } break; case R.id.reset: if(mediaPlayer != null){ mediaPlayer.seekTo(0); mediaPlayer.start(); status.setText("播放中"); } break; case R.id.stop: if(mediaPlayer != null){ mediaPlayer.stop(); mediaPlayer.release(); isStartPlaying = false; status.setText("停止"); } break; default: break; } } /** * 播放网络视频 * a. 创建并配置 MediaPlayer 对象 (音量, SurfaceHolder) * b. 为 MediaPlayer 设置错误监听器, 缓冲进度监听器, 播放完毕监听器, 准备完毕监听器 * c. 未 MediaPlayer 设置数据源 * d. 调用 prepare() 进入 Prapared 状态 * e. 调用 start() 进入 Started 状态 * * @param dataSource 播放视频的网络地址 */ private void playVideo(final String dataSource) { /* 点击播放有两种情况 * a. 第一次点击 : 需要初始化 MediaPlayer 对象, 设置监听器 * b. 第二次点击 : 只需要 调用 mediaPlayer 的 start() 方法 * 两种情况通过 isStartPlaying 点击时间判断 */ if(isStartPlaying){ /* 如果已经开始了播放, 就直接开始播放 */ mediaPlayer.start(); }else{ /* 如果是第一次开始播放, 需要初始化 MediaPlayer 设置监听器等操作 */ mediaPlayer = new MediaPlayer(); /* 创建 MediaPlayer 对象 */ mediaPlayer.setAudioStreamType(2); /* 设置播放音量 */ mediaPlayer.setDisplay(surface_holder); /* 设置播放载体 */ /* 设置 MediaPlayer 错误监听器, 如果出现错误就会回调该方法打印错误代码 */ mediaPlayer.setOnErrorListener(new OnErrorListener() { @Override public boolean onError(MediaPlayer arg0, int what, int extra) { System.out.println("MediaPlayer 出现错误 what : " + what + " , extra : " + extra); return false; } }); /* 设置缓冲进度更新监听器 */ mediaPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener() { @Override public void onBufferingUpdate(MediaPlayer arg0, int percent) { /* 打印缓冲的百分比, 如果缓冲 */ System.out.println("缓冲了的百分比 : " + percent + " %"); } }); /* 设置播放完毕监听器 */ mediaPlayer.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer arg0) { System.out.println("播放完毕了"); status.setText("播放完毕"); } }); /* 设置准备完毕监听器 */ mediaPlayer.setOnPreparedListener(new OnPreparedListener() { @Override public void onPrepared(MediaPlayer arg0) { System.out.println("准备完毕"); /* 设置播放状态 */ status.setText("播放中"); } }); new Thread(){ public void run() { try { System.out.println("设置数据源"); mediaPlayer.setDataSource(dataSource); mediaPlayer.prepare(); /* 打印播放视频的时长 */ System.out.println("视频播放长度 : " + mediaPlayer.getDuration()); mediaPlayer.start(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }; }.start(); /* 设置 MediaPlayer 开始播放标识为 true */ isStartPlaying = true; /* 设置播放状态 */ status.setText("正在缓冲"); } } /** * 在 Surface 大小发生改变的时候回调 * 实现的 SurfaceHolder.Callback 接口方法 */ @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { System.out.println("SurfaceHolder.Callback.surfaceChanged : Surface 大小发生改变"); } /** * 在 Surface 创建的时候回调, 一般在该方法中开始绘图 * 实现的 SurfaceHolder.Callback 接口方法 */ @Override public void surfaceCreated(SurfaceHolder arg0) { System.out.println("SurfaceHolder.Callback.surfaceCreated : Surface 开始创建"); } /** * 在 Surface 销毁之前回调, 在该方法中停止渲染线程, 释放相关资源 * 实现的 SurfaceHolder.Callback 接口方法 */ @Override public void surfaceDestroyed(SurfaceHolder arg0) { System.out.println("SurfaceHolder.Callback.surfaceDestroyed : Surface 销毁"); } @Override protected void onDestroy() { if(mediaPlayer != null) mediaPlayer.release(); super.onDestroy(); } }
4. 运行示例
日志信息 :
octopus@octopus:~/develop/adt-bundle-linux/sdk/tools$ adb logcat -s System.out --------- beginning of /dev/log/main --------- beginning of /dev/log/system I/System.out(21129): 设置数据源 I/System.out(21129): 视频播放长度 : 31200 I/System.out(21129): 准备完毕 I/System.out(21129): 缓冲了的百分比 : 0 % I/System.out(21129): 缓冲了的百分比 : 37 % I/System.out(21129): 缓冲了的百分比 : 37 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 缓冲了的百分比 : 100 % I/System.out(21129): 播放完毕了 I/System.out(21129): 缓冲了的百分比 : 100 %
作者:万境绝尘(octopus_truth@163.com)
转载请著名出处:http://blog.csdn.net/shulianghan/article/details/38895143
相关推荐
【Android应用源码全能...通过对这个项目的源码学习,开发者不仅能掌握Android视频播放的核心技术,还能了解如何设计和实现一个功能完善的网络视频播放器,这对于提升Android开发技能和理解多媒体处理有着极大的帮助。
这个"Android MediaPlayer 播放器(修改版)"应该是对原生`MediaPlayer`类进行了一些定制或优化,以适应特定的视频播放需求。在Android应用开发中,特别是在涉及多媒体内容时,`MediaPlayer`是一个不可或缺的部分。...
在Android平台上,开发一款网络...总的来说,开发“Android网络视频播放器”需要对Android多媒体框架、网络编程、UI设计和性能优化有深入理解。通过不断学习和实践,可以构建出功能完善且用户体验良好的视频播放应用。
总的来说,这篇毕业论文全面涵盖了Android应用开发中的网络通信、多媒体处理、用户界面设计以及性能优化等多个关键领域,对于希望在Android平台上构建网络视频播放器的开发者来说,具有很高的参考价值。
在Android平台上,开发一款视频播放器是一项常见的任务,它涉及到多媒体处理、用户界面设计和性能优化等多个技术领域。...对于希望深入理解Android多媒体开发的程序员来说,这是一个非常有价值的参考资料。
首先,`MediaPlayer`类是Android多媒体框架的一部分,它提供了丰富的功能,如播放本地和网络媒体文件、控制播放状态(播放、暂停、停止等)、调整音量以及处理各种播放事件。`MediaPlayer`的工作流程通常包括初始化...
本篇文章将深入探讨Android多媒体开发中的MediaPlayer源码,帮助开发者理解其工作原理并优化应用性能。 MediaPlayer是Android系统提供的一个接口,它负责加载、解码和播放音频及视频资源。在源码层面,MediaPlayer...
在安卓(Android)平台上开发网络视频播放器是一项技术密集型的任务,涉及到多个关键知识点。此项目可能是一个集成了多种技术的综合应用,旨在为用户提供流畅、高质量的在线视频观看体验。以下是一些核心的技术点: ...
总结起来,这款"Android视频播放器Java代码"涵盖了Android多媒体编程的核心知识点,包括`MediaPlayer`的使用、用户界面设计与交互、视频显示技术、网络流媒体播放以及性能优化。对于初学者而言,通过分析和学习这段...
在Android平台上开发网络视频播放器是一项复杂而技术性的工作,涉及到多媒体处理、网络通信、UI设计等多个方面。这个"android网络视频播放器(完整可运行).zip"压缩包提供了一个完整的源码示例,可以帮助开发者深入...
总的来说,"基于安卓Android的网络视频播放器源码"是一个涵盖Android基础、多媒体处理、网络通信、UI设计和性能优化等多个方面的综合实践项目。通过分析和研究这个源码,开发者可以深入理解Android应用开发的核心...
在Android开发中,MediaPlayer是一个非常重要的组件,它用于播放音频和视频文件。在这个"Android实例之MediaPlayer播放音乐和视频"中,我们...通过实践这个实例,你可以更好地理解和掌握Android多媒体播放的核心技术。
总之,这份"Android应用源码网络视频播放器"是一个宝贵的教育资源,涵盖了Android开发的多个重要方面,对于希望深入学习Android开发的学生来说,是一份难得的实践资料。通过研究和修改源代码,不仅可以提升编程技能...
通过研究这个源码,开发者不仅可以学习到Android多媒体播放的基础知识,还可以了解到如何将这些技术应用到实际项目中,提升开发技能。同时,这个源码也提供了与社区共享和交流的机会,共同进步。
在Android开发中,视频播放器是一个非常重要的组件,它允许用户在移动设备上观看各种视频内容。本教程将深入探讨Android SDK中的视频播放器——VideoPlayerE,它被设计为“傻瓜模式”,意味着它提供了简单易用的API...
在Android平台上,开发一个网络视频播放器是一项技术性较强的任务,涉及到多个关键知识点。这个"android网络视频播放器(完整可运行)"项目提供了一个实际的实现,让我们深入探讨其中涉及的技术栈和重要概念。 首先...
在Android应用开发中,MediaPlayer类是用于播放各种音频和视频资源的核心组件。在这个特定的示例中,我们关注的是如何...对于初学者来说,这个示例是一个很好的起点,有助于理解和掌握Android多媒体播放的基本操作。
在Android平台上,网络视频播放器的开发是一项关键的技术任务,涉及到多媒体处理、网络通信和用户交互等多个方面。本文将深入探讨基于Android的网络视频播放器源码,解析其核心概念和关键技术。 首先,"Android ...
在Android开发中,掌握MediaPlayer的使用是十分重要的,因为它是实现多媒体应用功能的基础。以下是对MediaPlayer的详细讲解:** **1. 初始化MediaPlayer** 初始化MediaPlayer对象是使用它的第一步。通常,我们需要...
综上所述,开发一个基于Android的网络视频播放器应用是一个综合性的任务,涵盖了Android平台的多个关键技术领域。通过深入学习和实践,开发者可以创建出高效、稳定且用户体验优秀的网络视频播放应用。