【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 多媒体开发】 MediaPlayer 网络视频播放器的更多相关文章
- 22_Android中的本地音乐播放器和网络音乐播放器的编写,本地视频播放器和网络视频播放器,照相机案例,偷拍案例实现
1 编写以下案例: 当点击了"播放"之后,在手机上的/mnt/sdcard2/natural.mp3就会播放. 2 编写布局文件activity_main.xml <Line ...
- Android应用开发:网络工具——Volley(二)
引言 在Android应用开发:网络工具--Volley(一)中结合Cloudant服务介绍了Volley的一般使用方法.当中包括了两种请求类型StringRequest和JsonObjectRequ ...
- [Android应用开发] 03.网络编程
*:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...
- Android应用开发--MP3音乐播放器代码实现(一)
需求1:将内存卡中的MP3音乐读取出来并显示到列表当中 1. 从数据库中查询所有音乐数据,保存到List集合当中,List当中存放的是Mp3Info对象 2. 迭代List集合,把每一个Mp3 ...
- Android多媒体开发(3)————使用Android NKD编译havlenapetr-FFMpeg-7c27aa2
1. 使用NDK去编译官方的FFmpeg原版的话,还得自己实现JNI层与Java层,工程量比较大.所以移植FFmpeg到Android平台时,可以移植一些已经实现JNI与JAVA层的开源项目,毕竟软件 ...
- 【Android 多媒体开发】 MediaPlayer 状态机 接口 方法 解析
作者 : 韩曙亮 转载请著名出处 : http://blog.csdn.net/shulianghan/article/details/38487967 一. MediaPlayer 状态机 介绍 ...
- Android开发 MediaPlayer将视频播放时尺寸适配完美
前言 视频播放有一个较为蛋疼的问题,那就是尺寸适配.如果不做尺寸适配视频将会变形拉伸或者压缩.下面我就介绍个人实现的算法. 满足一边的算法 满足一边?你可能是疑问是什么意思.意思是就是始终将视频的高度 ...
- Android 网络视频播放器
项目概要: 1.登录界面 2.播放列表 3.播放界面
- Android多媒体开发-stagefright及AwesomePlayer相关知识梳理
android的多媒体框架中, stagefright其实是AwesomePlayer的代理,就是个皮包公司. status_t StagefrightPlayer::setDataSource( c ...
随机推荐
- List Set Map比较
List按对象进入的顺序保存对象,不做排序或编辑操作. Set对每个对象只接受一次,并使用自己内部的排序方法(通常,你只关心某个元素是否属于Set,而不关心它的顺序–否则应该使用List). Map同 ...
- CentOS7 YUM 安装NGINX
1.先添加源: nano /etc/yum.repos.d/nginx.repo 把下边这段代码添加到nginx.repo中去.[nginx] name=nginx repo baseurl=http ...
- 毕业回馈-89c51之定时器/计数器(Timer/Count)
今天分享的是89c51系列单片机的内部资源定时器/计数器,在所有的嵌入式系统中都包含这两个内部功能. 首先先了解几个定时器/计数器相关的概念: •时钟周期:时钟周期 T 是时序中最小的时间单位,具体计 ...
- Kirill And The Game CodeForces - 842A
CodeForces - 842A 需要将除法改换成乘法进行计算 #include<bits/stdc++.h> using namespace std; int main() { lon ...
- MySQL DELETE 语句
MySQL DELETE 语句 你可以使用 SQL 的 DELETE FROM 命令来删除 MySQL 数据表中的记录. 你可以在mysql>命令提示符或PHP脚本中执行该命令. 语法 以下是S ...
- docker volume创建、备份、nfs存储
docker存储volume #环境 centos7.4 , Docker version 17.12.0-ce docker volume创建.备份.nfs存储 #docker volume 数据存 ...
- Java语言程序设计课程学期总结
2016-2017 第2学期 课程介绍 编程类课程,76学时(44理论+32实验),学期末还有1周的课程设计. 问题与现状 4个班共120人,教师无法逐一检查每个学生的编程实验. 纸质作业质量不高. ...
- How To determine DDIC Check Table, Domain and Get Table Field Text Data For Value?
How To determineDDIC Check Table, Domain and Get Table Field Text Data For Value? 1.Get Table Fie ...
- Common-used commands in Docker
1. Start running a image in background mode docker run -it -d <image>:<tag> e.g. docker ...
- 快速了解 Robot Operating System(ROS) 机器人操作系统
http://www.ros.org/ 关于ROS About ROS http://www.ros.org/about-ros/ 机器人操作系统(ROS)是用于编写机器人软件的灵活框架.目的在简化 ...