Android 系列笔记 九 - MediaPlayer

图片大小的计算:图片的像素 * 每个像素所占的大小

  1. 单色位图:只能表示两种颜色,使用两个数字 0 与 1 表示,使用一个长度为 1 的二进制数组就可以表示了,每个像素占用 1/8 个字节
  2. 16 色位图:能表示 16 种颜色,需要 16 个数字, 0 - 15, 0000 - 1111
    使用一个长度为 4 的二进制数组就能够表示了,每个像素占用 1/2 个字节
  3. 256 色位图:能表示 256 种颜色
    需要 256 个数字:0 - 255, 0000 0000 - 1111 1111
    使用一个长度为 8 的二进制数字
    每个像素占用 1 个字节
  4. 24 位位图
    每个像素占用 24 位,也就是 3 个字节,所以叫 24 位位图
    R: 0 - 255
    G: 0 - 255
    B: 0 - 255

利用缩放加载大图片

计算机把图片所有像素信息全部解析出来,保存至内存
Android 保存图片像素信息,是用 ARGB 保存的,每个像素占用 4 个字节
手机屏幕 320 x 480,总像素 153600
图片宽高 2400 x 3200,总像素 7680000
2400 / 320 = 7
3200 / 480 = 6
用大的数来缩放,这样才可以在屏幕上显示完整的图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public void click(View v){
Options opt = new Options();
//不为像素申请内存,只获取图片宽高
opt.inJustDecodeBounds = true;
BitmapFactory.decodeFile("/sdcaed/dog.jpg", opt);
int width = opt.outWidth();
int height = opt.outHeight();

//获取屏幕宽高
Display dp = getWindowManager().getDefaultDisplay();
int screenWidth = dp.getWidth();
int screenHeight = dp.getHeight();
//api 13 才能使用
//dp.getSize(new Point())

//计算缩放比例
int scale = 1;
int scaleWidth = width / screenWidth;
int scaleHeight = height /screenHeight;
scale = scaleWidth >= screenHeight ? scaleWidth : scaleHeight;

if(scaleWidth >= scaleHeight && scaleWidth >= 1){
scale = scaleWidth;
}else if(scaleWidth < scaleHeight && scaleHeight >= 1){
scale = scaleHeight;
}
//设置缩放比例
opt.inSampleSize = scale;
opt.inJustDecodeBounds = false;
Bitmap bm = BitmapFactory.decodeFile("/sdcaed/dog.jpg", opt);

imageView.setImageBitmap(bm);
}

创建图片副本

在内存中创建一个图片的拷贝

1
2
3
4
5
6
7
8
9
10
11
12
//这个对象是只读的
Bitmap bm = BitmapFactory.decodeFile("/sdcaed/dog.jpg");
//创建图片副本
//在内存中创建一个与原图一模一样大小的bitmap对象,里面还没有绘制任何内容
//该对象可读可写
Bitmap bmCopy = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
//创建画笔
Paint paint = new Paint();
//创建画板对象
Canvas canvas = new Canvas(bmCopy);
//开始绘制
canvas.drawBitmap(bm, new Matrix(), paint);

简单特效处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Matrix matrix = new Matrix();
//平移 将顶点相对于bitmap平移至(20, 40)
matrix.setTranslate(20, 40);
//以图片右下角顶点缩放 宽放大两倍,高缩小到0.5倍
matrix.setScale(2, 0.5f);
//以图片中心点缩放 宽放大两倍,高缩小到0.5倍
matrix.setScale(2, 0.5f, bm.getWidth() /2, bm.getHeight() / 2);
//旋转 相对左上角
matrix.setRotate(45);
//旋转中心点在中心
matrix.setRotate(45, bm.getWidth() /2, bm.getHeight() /2);

//镜面效果
matrix.setScale(-1, 1);
matrix.postTranslate(bm.getWidth(), 0);
//倒影效果
matrix.setScale(1, -1);
matrix.postTranslate(0, bm.getHeight());

触摸事件:画板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Bitmap bm = BitmapFactory.decodeResources(getResources(), R.drawable.bg);
Bitmap bmCopy = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(4);
//创建画板对象
Canvas canvas = new Canvas(bmCopy);
//开始绘制
canvas.drawBitmap(bm, new Matrix(), paint);
imageView.setImageBitmap(bmCopy);

imageView.setOnTouchListener(new OnTouchListener(){
int startX;
int startY;

@Override
public boolean onTouch(View v, MotionEvent event){
int action = event.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:
startX = (int)event.getX();
startY = (int)event.getY();
break;
case MotionEvent.ACTION_MOVE:
int x = (int)event.getX();
int y = (int)event.getY();
canvas.drawLine(startX, startY, x, y, paint);
imageView.setImageBitmap(bmCopy);
startX = x;
startY = y;
break;
case MotionEvent.ACTION_UP:
break;
}
//返回 true: 告诉系统,这个触摸事件由我处理
//返回 false: 告诉系统,这个事件我不处理,这时系统会把触摸事件传递给 imageView 的父节点
return true;
}
});

画板图片的保存

SD卡每次准备的时候,系统会遍历SD卡中的所有文件,系统会把所有的多媒体文件都放在一个MediaStore数据库中生成一个索引,数据库中保存了文件的文件名、路径、大小、长度、艺术家等。
图库、音乐、视频每次启动时,其实不会去遍历SD卡寻找多媒体文件,而是从MediaStore数据库中读取多媒体文件,通过库中的索引找到相对应的多媒体文件后,把文件显示在界面

1
2
3
4
5
6
7
8
File file = new File(getFilesDir(), "zuopin.png");
FileOutputStream fos = new FileOutputStream(file);
bmCopy.compress(CompressFormat.PNG, 100, fos);

Intent intent = new Intent();
intent.setAction(Intent.ACTION_MEDIA_MOUNTED);
intent.setData(Uri.fromFile(Environment.getExternalStorageDirectory()));
sendBroadcast(intent);

播放网络音乐

1
2
3
4
5
6
interface MusicInterface{
void play();
void pause();
void continuePlay();
void seekTo(int position);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
class MusicService extends Service{
MediaPlayer mediaPlayer;
Timer timer;

@Override
public IBinder onBind(Intent intent){
return new MusicController();
}

@Override
public void onCreate(){
mediaPlayer = new MediaPlayer();
}

@Override
public void onDestory(){
super.onDestory();
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
if(timer != null){
timer.cancel();
timer = null;
}
}

class MusicController extends Binder implements MusicInterface{
@Override
public void play(){
MusicService.this.play();
}
@Override
public void pause(){
MusicService.this.pause();
}
@Override
public void continuePlay(){
MusicService.this.continuePlay();
}
@Override
public void seekTo(int position){
MusicService.this.seekTo(position);
}
}

public void play(){
musicPlayer.reset();
try{
//播放本地音乐
//musicPlayer.setDataSource(getFilesDir + "/a.mp3");
//musicPlayer.prepare();
//musicPlayer.start();

//播放网络音乐
musicPlayer.setDataSource("http://hello.com/bzd.mp3");
musicPlayer.prepareAsync();
musicPlayer.setOnPreparedListener(new OnPreparedListener(){
@Override
public void onPrepared(MediaPlayer player){
player.start();
addTimer();
}
});
}catch(Exception e){
e.printStackTrace();
}
}

public void continuePlay(){
musicPlayer.start();
}

public void pause(){
musicPlayer.pause();
}

public void seekTo(int position){
musicPlayer.seekTo(position);
}


public void addTimer(){
if(timer == null)
timer = new Timer();
timer.schedule(new TimerTask(){
//获取歌曲总时长
int duration = musicPlayer.getDuration();
//获取歌曲当前播放进度
int position = musicPlayer.getCurrentPosition();
Message msg = Message.obtain();
Bundle bundle = new Bundle();
bundle.putInt("duration", duration);
bundle.putInt("position", position);
msg.setData(bundle);
MainActivity.handler.sendMessage(msg)
}, 5, 500);//开始计时任务后的5毫秒,第一次执行run方法,以后每500毫秒执行一次
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class MainActivity extends Activity{
MusicInterface interface;
MyServiceConnection conn;
Intent intent;
static SeekBar seekBar;

static Handler handler =new Handler(){
public void handleMessage(Message msg){
Bundle bundle = msg.getData();
int duration = bundle.getInt("duration");
int position = bundle.getInt("position");
seekBar.setMax(duration);
seekBar.setProgress(position);
}
}

@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

seekBar = (SeekBar)findViewById(R.id.seekbar);
seekBar.setOnSeekBarChangeListener(new MyOnSeekBarChangeListener());

intent = new Intent(this, MusicService.class);
startService(intent);
conn = new MyServiceConnection();
bindService(intent, conn, BIND_AUTO_CREATE);
}

class MyOnSeekBarChangeListener implements OnSeekBarChangeListener{
@Override
public void onStopTrackingTouch(SeekBar seekBar){
//拖动seekBar后,改变播放进度
int position = seekBar.getProgress();
interface.seekTo(position);
}

@Override
public void onStartTrackingTouch(SeekBar seekBar){

}

@Override
public void onProgressChanged(SeekBar seekBar,
int progress, boolean fromUser){

}
}

class MyServiceConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service){
interface = (MusicInterface)service;
}
@Override
public void onServiceDisconnected(ComponentName name){

}
}

public void play(View v){
interface.play();
}

public void pause(View v){
interface.pause();
}

public void continuePlay(View v){
interface.continuePlay();
}

//退出应用 并销毁服务
public void exit(View v){
unbindService(conn);
stopService(intent);
}
}

播放视频 MediaPlayer + SurfaceView

双缓冲技术
重量级组件
只要不可见,就不会创建,可见时才会创建
只要不可见,就会销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class MainActivity extends Activity{
MediaPlayer player;
SurfaceView sv;
static int currentPosition;

protected coid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

sv= (SurfaceView)findViewById(R.id.surfaceView);
final SurfaceHolder sh = sv.getHolder();

sh.addCallback(new Callback(){
@Override
public void surfacceDestoryed(SurfaceHolder holder){
if(plyer != null){
currentPosition = player.getCurrentPosition();
player.stop();
player.release();
player = null;
}
}

@Override
public void surfacceCreated(SurfaceHolder holder){
if(player == null){
player = new MediaPlayer();
player.reset();
try{
player.setDataSource(getFilesDir + "/a.mp4");
palyer.setDisplay(holder);
player.prepare();
player.start();
player.seekTo(currentPosition);
}catch(Exception e){
e.printStackTrace();
}
}
}

@Override
public void surfacceChanged(SurfaceHolder holder,
int format, int width, int height){

}
});
}
}

VideoView

1
2
3
//本地播放
video.setVideoPath(getFilesDir + "/a.mp4");
video.start();

FFMPEG

开源免费的音视频编解码器

Vitamio 视频播放第三方框架

封装了 FFMPEG 的视频播放框架
对外提供的 API 全部都是 java api
封装的有 VideoView

1
2
3
4
5
6
7
//检测硬件是否支持 Vitamio 引擎
if(!LibsChecker.checkVitamioLibs(this)){
return;
}
video.setVideoPath(getFilesDir + "/a.rmvb");
video.start();
video.setMediaController(new MediaController(this));

拍照与摄像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public image(View v){
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
File file = new File(getFilesDir + "/a.jpg");
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
startActivityForResult(intent, 10);
}

protected void onActivityResult(int requestCode, int responseCode, Intent data){
super.onActivityResult(requestCode, responseCode, intent);
if(requestCode == 10){
//拍照成功
}else if(requestCode == 20){
//摄像成功
}

public video(View v){
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_VIDEO_CAPTURE);
File file = new File(getFilesDir + "/a.3gp");
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
//第二个属性 0: 低质 1: 高质
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);

startActivityForResult(intent, 20);
}