Android Audio控制和MediaButton远程控制(音视频控制配合)

使用过Android系统的朋友应该都知道,Android里面声音是区分好几种情况,每种情况下的音频大小是独立的。也就是说你调节了电话铃声大小不会影响多媒体播放的声音大小。这个涉及了AudioStream的使用,今天会详细讲解一下AudioStream相关知识。另外我们用耳机上按钮控制音乐播放器等音频程序,可以使用MediaButton来实现远程控制。另外会详细讲解MediaButton的两种注册方法以及他们的区别。

1、AudioStream分类

  首先看看AudioStream的分类,Android为不同的应用场合定义了不同的AudioStream: Voice Call, Ring, Music,Alarm, Notification, DTMF。 这些AudioStream是相互独立的,所以也有各自的音量。AudioStream的定义在android.media.AudioManager中。

 

 

2、AudioStream控制

  但我们按下调音量大小的按键,缺省情况下,按下音量控制的硬件控制键Vol+/-,调节的是当前被激活的AudioStream的音量,如果你的程序当前没有正在播放任何声音,按下Vol+/-调节的是来电铃声的音量。在 某一个程序运行时,希望按下Vol+/-调节的是当前所使用的AudioStream的音量,Android在Activity中提供了 setVolumeControlStream()方法用来指定你的应用程序使用的Audio Stream类型。所以,如果你的程序用到Audio的播放,你首先要知道你的程序所用的AudioStream类型,并在onCreate()中调用setVolumeControlStream()来设定AudioStream的类型。

  例如下面是音乐播放器打开的时候,设置的AudioStream:

 this.setVolumeControlStream(AudioManager.STREAM_MUSIC);

 

 

3、MediaButton

   这里说的MediaButton其实是对应系统的 ACTION_MEDIA_BUTTON,ACTION_MEDIA_BUTTON的定义为 “android.intent.action.MEDIA_BUTTON”,是系统控制远程音乐和其他多媒体的方法。在手机上常见的应用就是耳机上的控 制按钮。一般我们手机或者平板上的音乐、视频播放器都会响应这个广播。例如Android系统自带的Music工程,里面就响应了这个广播。

  除了在手机平板这些对音视频控制,其实在智能电视或者对于需要高度整合的系统来说,这个消息都十分有用。例如:智能电视里面,我们可以利用这个广播实现远程遥控,系统底层接收遥控播放、暂停、下一曲、上一曲等消息,然后通过ACTION_MEDIA_BUTTON广播给上层应用。高度整合的系统,一般都是针对深度定制的系统,用于特定环境的系统。这种系统对于应用之间的配合要求十分高。例如一些工控的机器。这些系统定制比一般的手机系统要求高很多,这些情况下,ACTION_MEDIA_BUTTON就可以发挥很重要的作用。

  下面我们看看怎么使用ACTION_MEDIA_BUTTON这个广播。

?
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
//Edited by mythou
 
public  class  RemoteControlClientReceiver extends  BroadcastReceiver
{
     @SuppressWarnings ( "unused" )
     private  static  final  String TAG = "mythou_ACTION_MEDIA_BUTTON" ;
 
     /*
      * It should be safe to use static variables here once registered via the
      * AudioManager
      */
     private static long mHeadsetDownTime = 0;
     private static long mHeadsetUpTime = 0;
 
     @Override
     public void onReceive(Context context, Intent intent)
     {
     //获取对应Acton,判断是否是需要的ACTION_MEDIA_BUTTON
         String action = intent.getAction();if (action.equalsIgnoreCase(Intent.ACTION_MEDIA_BUTTON))
         {
             KeyEvent event = (KeyEvent) intent
                     .getParcelableExtra(Intent.EXTRA_KEY_EVENT);
             if (event == null)
                 return;
 
             if (event.getKeyCode() != KeyEvent.KEYCODE_HEADSETHOOK
                     && event.getKeyCode() != KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
                     && event.getAction() != KeyEvent.ACTION_DOWN)
                 return;
 
             Intent i = null;
             switch (event.getKeyCode())
             {
             /*
              * one click => play/pause long click => previous double click =>
              * next
              */
         //这里根据按下的时间和操作,分离出具体的控制
             case  KeyEvent.KEYCODE_HEADSETHOOK:
             case  KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                 long  time = SystemClock.uptimeMillis();
                 switch  (event.getAction())
                 {
                 case  KeyEvent.ACTION_DOWN:
                     if  (event.getRepeatCount() > 0 )
                         break ;
                     mHeadsetDownTime = time;
                     break ;
                 case  KeyEvent.ACTION_UP:
                     // long click
                     if  (time - mHeadsetDownTime >= 1000 )
                     {
                         i = new  Intent(AudioService.ACTION_REMOTE_BACKWARD);
                         time = 0 ;
                         // double click
                     } else  if  (time - mHeadsetUpTime <= 500 )
                     {
                         i = new  Intent(AudioService.ACTION_REMOTE_FORWARD);
                     }
                     // one click
                     else
                     {
                         if  (mLibVLC.isPlaying())
                             i = new  Intent(AudioService.ACTION_REMOTE_PAUSE);
                         else
                             i = new  Intent(AudioService.ACTION_REMOTE_PLAY);
                     }
                     mHeadsetUpTime = time;
                     break ;
                 }
                 break ;
        //下面是常规的播放、暂停、停止、上下曲 
             case  KeyEvent.KEYCODE_MEDIA_PLAY:
                 i = new  Intent(AudioService.ACTION_REMOTE_PLAY);
                 break ;
             case  KeyEvent.KEYCODE_MEDIA_PAUSE:
                 i = new  Intent(AudioService.ACTION_REMOTE_PAUSE);
                 break ;
             case  KeyEvent.KEYCODE_MEDIA_STOP:
                 i = new  Intent(AudioService.ACTION_REMOTE_STOP);
                 break ;
             case  KeyEvent.KEYCODE_MEDIA_NEXT:
                 i = new  Intent(AudioService.ACTION_REMOTE_FORWARD);
                 break ;
             case  KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                 i = new  Intent(AudioService.ACTION_REMOTE_BACKWARD);
                 break ;
             }
 
             if  (isOrderedBroadcast())
                 abortBroadcast();
             if  (i != null )
                 context.sendBroadcast(i);
         }
     }
}

 

  上面是音乐播放器里面的一个ACTION_MEDIA_BUTTON 的接收器。接收ACTION_MEDIA_BUTTON  跟我们一般的广播差不多,也是需要定义一个BroadcastReceiver,如何定义BroadcastReceiver这里不多说。

  从上面的代码可以看到,我们可以根据接收的ACTION判断是否是ACTION_MEDIA_BUTTON ,然后获取里面的KeyEvent的值来判断是什么操作。里面包含了常规的媒体控制的值。编写完BroadcastReceiver后,我们只需要在AndroidManifest.xml注册即可。

?
1
2
3
4
5
6
7
8
//Edited by mythou
 
    <receiver android:name= "RemoteControlClientReceiver"  >
             <intent-filter>
                 <action android:name= "android.intent.action.MEDIA_BUTTON"  />
             </intent-filter>
         </receiver>

4、独占MEDIA_BUTTON广播

  上面说的注册方法是一般情况下使用,但是 ACTION_MEDIA_BUTTON 比较特殊,你可以注册一个ACTION_MEDIA_BUTTON 。只有你一个能接收到,其他人都不能接收。因为这个是控制多媒体的,所以存在一个音视频冲突问题。做过一些系统整合的朋友应该都遇到过这个问题。基本上在 我工作里面,我最不想处理的就是音视频冲突,特别在多任务系统里面。

  下面我们说说如何注册一个只有你能接收的ACTION_MEDIA_BUTTON :

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Edited by mythou
 
//获取音频服务
AudioManager audioManager = (AudioManager) this .getSystemService(AUDIO_SERVICE);
//注册接收的Receiver
ComponentName mRemoteControlClientReceiverComponent;
mRemoteControlClientReceiverComponent = new  ComponentName(
                 getPackageName(), RemoteControlClientReceiver. class .getName());
//注册MediaButton
audioManager.registerMediaButtonEventReceiver(mRemoteControlClientReceiverComponent);
 
复制代码
 
不需要的时候,只要取消注册即可:
 
//取消注册
audioManager.unregisterMediaButtonEventReceiver(mRemoteControlClientReceiverComponent);

5、两种注册情况对比

  下面说说两种注册ACTION_MEDIA_BUTTON不同的地方,需要说这个就要讲解一下ACTION_MEDIA_BUTTON的发送机制。这个主要是AudioManager做的事情,AudioManager是上层的一个高层封装的音频管理类,真正实现音频的管理的是IAudioService 实现,这里面涉及了Android的底层核心Binder机制,这个是分析Android系统层必须掌握的课程。今天主要是讲解ACTION_MEDIA_BUTTON,所以我这里不做深入分析Binder机制和下面的IAudioService ,后面有空我会写一些分析Binder机制的文章。

  下面简单说一下ACTION_MEDIA_BUTTON消息是如何广播分发的,让大家使用的时候知道何时使用哪种注册方式。

A、AudioManager或者说AudioService服务端对象内部会利用一个栈来管理所有registerMediaButtonEventReceiver()注册的ComponentName对象,最后调用registerMediaButtonEventReceiver()注册的ComponentName就位置这个栈的栈顶。

B、当系统发送MEDIA_BUTTON,系统MediaButtonBroadcastReceiver 监听到系统广播,它会做如下处理:

  • 如果栈为空,则所有注册了该Action的广播都会接受到,因为它是由系统发送的。
  • 如果栈不为空,那么只有栈顶的那个广播能接受到MEDIA_BUTTON的广播,手动发送了MEDIA_BUTTON广播,并且指定了目标对象(栈顶对象)去处理该MEDIA_BUTTON 。

  上面的两条规则,大家一定要记住,这个关系我们注册的ACTION_MEDIA_BUTTON能否正常工作。当然这个是系统全局广播,需要大家的APP都遵守这个规则才可以让系统正常运行。


转载地址:http://www.kwstu.com/ArticleView/kwstu_2013968333952

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值