管理音频焦点

情景:当你的app隐退到后台,而其他也有播放能力的app浮现在前台,这个时候,你可能要暂停你原有app的播放功能,和解除监听Media Button,把控制权交给前台的APP。

这就需要监听音频的焦点。

在开始播放之前,请求焦点,使用AudioManagerrequestAudioFocus方法。

当你请求音频焦点,你可以指定你要监听的流类型(比如STREAM_MUSIC)和指定你要占有焦点多久。

当然从编程的角度来看,app获取焦点,其它app失去焦点,你应该都需要有所反应。

示例:请求音频焦点

01 AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
02  
03 // Request audio focus for playback
04 int result = am.requestAudioFocus(focusChangeListener,
05                    // Use the music stream.
06                    AudioManager.STREAM_MUSIC,
07                    // Request permanent focus.
08                    AudioManager.AUDIOFOCUS_GAIN);
09  
10 if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
11    mediaPlayer.start();
12 }

应对失去焦点的监听:

01 private OnAudioFocusChangeListener focusChangeListener =
02   new OnAudioFocusChangeListener() {
03  
04   public void onAudioFocusChange(int focusChange) {
05      AudioManager am =
06        (AudioManager)getSystemService(Context.AUDIO_SERVICE);
07  
08      switch (focusChange) {
09        case (AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) :
10           // Lower the volume while ducking.
11           mediaPlayer.setVolume(0.2f, 0.2f);
12           break;
13  
14        case (AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) :
15           pause();
16           break;
17  
18        case (AudioManager.AUDIOFOCUS_LOSS) :
19           stop();
20           ComponentName component =
21             new ComponentName(AudioPlayerActivity.this,
22                MediaControlReceiver.class);
23           am.unregisterMediaButtonEventReceiver(component);
24           break;
25  
26        case (AudioManager.AUDIOFOCUS_GAIN) :
27           // Return the volume to normal and resume if paused.
28           mediaPlayer.setVolume(1f, 1f);
29           mediaPlayer.start();
30           break;
31  
32        default: break;
33  
34      }
35   }
36 };

放弃音频焦点:

1 AudioManager am =
2    (AudioManager)getSystemService(Context.AUDIO_SERVICE);
3  
4 am.abandonAudioFocus(focusChangeListener);

当你戴上耳机的时候,你可能需要降低音量或者先暂停播放,如何监听这种输出方式的改变呢?

答:

1 private class NoisyAudioStreamReceiver extends BroadcastReceiver {
2    @Override
3    public void onReceive(Context context, Intent intent) {
4      if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals
5         (intent.getAction())) {
6         pause();
7      }
8    }
9 }

录音

使用AudioRecord类去录音。创建一个AudioRecorder,指定资源,频率,通道配置,音频编码,和缓冲区大小

1 int bufferSize = AudioRecord.getMinBufferSize(frequency,
2                                 channelConfiguration,
3                                  audioEncoding);
4 AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
5                                  frequency, channelConfiguration,
6                                  audioEncoding, bufferSize);

频率、音频编码、和通道配置会影响录音的大小和质量。

出去私有的考虑,Android需要RECORD_AUDIO权限:

1 <uses-permission android:name=”android.permission.RECORD_AUDIO”/>

当AudioRecorder对象被初始化,然后可以通过startRecording方法去开始异步录音,使用read方法将原始的音频数据放入录音缓冲区:

1 audioRecord.startRecording();
2 while (isRecording) { 
3   [ ... populate the buffer ... ]
4   int bufferReadResult = audioRecord.read(buffer, 0, bufferSize);
5 }

录下的原始音频数据后,拿什么播放呢?

答:使用AudioTrack去播放该类音频。

录音的例子:

01 int frequency = 11025;
02 int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
03 int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
04  
05 File file =
06    new File(Environment.getExternalStorageDirectory(), “raw.pcm”);
07  
08 // Create the new file.
09 try {
10    file.createNewFile();
11 } catch (IOException e) {
12    Log.d(TAG, “IO Exception”, e);
13 }
14  
15 try {
16    OutputStream os = new FileOutputStream(file);
17    BufferedOutputStream bos = new BufferedOutputStream(os);
18    DataOutputStream dos = new DataOutputStream(bos);
19  
20    int bufferSize = AudioRecord.getMinBufferSize(frequency,
21                                                           channelConfiguration,
22                                                           audioEncoding);
23    short[] buffer = new short[bufferSize];
24  
25    // Create a new AudioRecord object to record the audio.
26    AudioRecord audioRecord =
27      new AudioRecord(MediaRecorder.AudioSource.MIC,
28                         frequency,
29                         channelConfiguration,
30                         audioEncoding, bufferSize);
31     audioRecord.startRecording();
32  
33    while (isRecording) {
34     int bufferReadResult = audioRecord.read(buffer, 0, bufferSize);
35     for (int i = 0; i < bufferReadResult; i++)
36        dos.writeShort(buffer[i]);
37   }
38  
39   audioRecord.stop();
40   dos.close();
41 } catch (Throwable t) {
42   Log.d(TAG, “An error occurred during recording”, t);
43 }

AudioTrack播放声音

1 AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
2                                               frequency,
3                                               channelConfiguration,
4                                               audioEncoding,
5                                               audioLength,
6                                               AudioTrack.MODE_STREAM);

注意前面的参数要与你之前录音的参数一致。

1 audioTrack.play();
2 audioTrack.write(audio, 0, audioLength);

write方法将原始的音频数据加入到播放缓冲区中。

创建Sound Pool

一般用来播放短促的声音,支持多音频同步播放。

直接看例子:

01 int maxStreams = 10;
02 SoundPool sp = new SoundPool(maxStreams, AudioManager.STREAM_MUSIC, 0);
03  
04 int track1 = sp.load(this, R.raw.track1, 0);
05 int track2 = sp.load(this, R.raw.track2, 0);
06 int track3 = sp.load(this, R.raw.track3, 0);
07  
08 track1Button.setOnClickListener(new OnClickListener() {
09   public void onClick(View v) {
10      sp.play(track1, 1, 1, 0, -1, 1);
11   }
12 });
13  
14 track2Button.setOnClickListener(new OnClickListener() {
15   public void onClick(View v) {
16      sp.play(track2, 1, 1, 0, 0, 1);
17   }
18 });
19  
20 track3Button.setOnClickListener(new OnClickListener() {
21   public void onClick(View v) {
22      sp.play(track3, 1, 1, 0, 0, 0.5f);
23   }
24 });
25  
26 stopButton.setOnClickListener(new OnClickListener() {
27   public void onClick(View v) {
28      sp.stop(track1);
29      sp.stop(track2);
30      sp.stop(track3);
31   }
32 });
33  
34 chipmunkButton.setOnClickListener(new OnClickListener() {
35   public void onClick(View v) {
36      sp.setRate(track1, 2f);
37   }
38 });

Android2.2(Api Level 8)引入两个非常方便的方法,autoPauseautoResume,分别会暂停和运行状态,所有活跃的音频流。

若不再需要这些音频集合,就可以soundPool.release();去释放资源。

照相机拍照

使用Intents去拍照:

1 startActivityForResult(
2   new Intent(MediaStore.ACTION_IMAGE_CAPTURE), TAKE_PICTURE);

当然对应的onActivityResult,默认的返回的照片会以缩略图的形式。

如果想获取完整大小的图片,则需要先指定存储的目标文件,下面例子展示:

01 // Create an output file.
02 File file = new File(Environment.getExternalStorageDirectory(),
03                           “test.jpg”);
04 Uri outputFileUri = Uri.fromFile(file);
05  
06 // Generate the Intent.
07 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
08 intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
09  
10 // Launch the camera app.
11 startActivityForResult(intent, TAKE_PICTURE);

注意:一旦你以这种方式启动后,就不会有缩略图返回了,所以所接收到得Intent将为null。

下面这个例子的onActivityResult对这两种情况做了处理:

01 @Override
02 protected void onActivityResult(int requestCode,
03                                        int resultCode, Intent data) {
04    if (requestCode == TAKE_PICTURE) {
05      // Check if the result includes a thumbnail Bitmap
06      if (data != null) {
07         if (data.hasExtra(“data”)) {
08           Bitmap thumbnail = data.getParcelableExtra(“data”);
09           imageView.setImageBitmap(thumbnail);
10         }
11      } else {
12         // If there is no thumbnail image data, the image
13         // will have been stored in the target output URI.
14  
15         // Resize the full image to fit in out image view.
16         int width = imageView.getWidth();
17         int height = imageView.getHeight();
18  
19         BitmapFactory.Options factoryOptions = new
20           BitmapFactory.Options();
21  
22         factoryOptions.inJustDecodeBounds = true;
23         BitmapFactory.decodeFile(outputFileUri.getPath(),
24                                      factoryOptions);
25  
26        int imageWidth = factoryOptions.outWidth;
27        int imageHeight = factoryOptions.outHeight;
28  
29        // Determine how much to scale down the image
30        int scaleFactor = Math.min(imageWidth/width,
31                                        imageHeight/height);
32  
33        // Decode the image file into a Bitmap sized to fill the View
34        factoryOptions.inJustDecodeBounds = false;
35        factoryOptions.inSampleSize = scaleFactor;
36        factoryOptions.inPurgeable = true;
37  
38        Bitmap bitmap =
39          BitmapFactory.decodeFile(outputFileUri.getPath(),
40                                        factoryOptions);
41  
42        imageView.setImageBitmap(bitmap);
43     }
44   }
45 }

直接控制照相机

首先这个少不了:

1 <uses-permission android:name=”android.permission.CAMERA”/>

获取Camera通过:


Camera camera = Camera.open();

当你使用完了,记得释放资源哦:


camera.release();

照相机的属性

1 Camera.Parameters parameters = camera.getParameters();

通过此,你可以找到很多关于照相机的属性,有些参数是基于平台版本的。

你可以获得焦点的长度,还有相对水平和垂直的角度,分别通过getFocalLengthget[Horizontal/Vertical]ViewAngle

Android 2.3(Api Level 9)引入getFocusDistance方法,你可以用来估计镜头和对象之间的距离,此方法会注入一个浮点数组,包含近、远、最优焦点距离;

01 float[] focusDistances = new float[3];
02  
03 parameters.getFocusDistances(focusDistances);
04  
05 float near =
06   focusDistances[Camera.Parameters.FOCUS_DISTANCE_NEAR_INDEX];
07 float far =
08    focusDistances[Camera.Parameters.FOCUS_DISTANCE_FAR_INDEX];
09 float optimal =
10    focusDistances[Camera.Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX];

照相机设置和图像参数

设置参数的方法,类似于set*,从而修改Parameter对象,修改完之后:

camera.setParameters(parameters);

具体参数细节就不介绍了。

使用照相机预览

同样SurfaceView又派上用场了。

看段框架代码

01 public class CameraActivity extends Activity implements
02    SurfaceHolder.Callback {
03  
04    private static final String TAG = “CameraActivity”;
05  
06    private Camera camera;
07  
08    @Override
09    public void onCreate(Bundle savedInstanceState) {
10      super.onCreate(savedInstanceState);
11      setContentView(R.layout.main);
12  
13      SurfaceView surface = (SurfaceView)findViewById(R.id.surfaceView);
14      SurfaceHolder holder = surface.getHolder();
15      holder.addCallback(this);
16      holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
17      holder.setFixedSize(400, 300);
18    }
19  
20    public void surfaceCreated(SurfaceHolder holder) {
21      try {
22        camera.setPreviewDisplay(holder);
23        camera.startPreview();
24        // TODO Draw over the preview if required.
25      } catch (IOException e) {
26        Log.d(TAG, “IO Exception”, e);
27      }
28    }
29  
30  
31   public void surfaceDestroyed(SurfaceHolder holder) {
32     camera.stopPreview();
33   }
34  
35   public void surfaceChanged(SurfaceHolder holder, int format,
36                                   int width, int height) {
37   }
38  
39   @Override
40   protected void onPause() {
41     super.onPause();
42     camera.release();
43   }
44  
45   @Override
46   protected void onResume() {
47     super.onResume();
48     camera = Camera.open();
49   }
50 }

调用camerasetPreviewCallback方法,传入一个PreviewCallback的实现,重写onPreviewFrame方法。

01 camera.setPreviewCallback(new PreviewCallback() {
02   public void onPreviewFrame(byte[] data, Camera camera) {
03      int quality = 60;
04  
05      Size previewSize = camera.getParameters().getPreviewSize();
06      YuvImage image = new YuvImage(data, ImageFormat.NV21,
07        previewSize.width, previewSize.height, null);
08      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
09  
10      image.compressToJpeg(
11        new Rect(0, 0,previewSize.width, previewSize.height),
12           quality, outputStream);
13  
14       // TODO Do something with the preview image.
15   }
16 });

Android 4.0加入了人脸识别的API这里就不多说了。

拍照

前面这些都配置过了,那么如何拍照呢?

答:使用camera对象的takePicture方法,传入一个ShutterCallback和两个PictureCallback实现(一个为了RAW,另外一个为了JPEG编码的图像)。

例子:框架代码,拍照和保存JPEG图像到SD卡:

01 private void takePicture() {
02    camera.takePicture(shutterCallback, rawCallback, jpegCallback);
03 }
04  
05 ShutterCallback shutterCallback = new ShutterCallback() { 
06    public void onShutter() {
07      // TODO Do something when the shutter closes.
08    }
09 };
10  
11 PictureCallback rawCallback = new PictureCallback() {
12    public void onPictureTaken(byte[] data, Camera camera) {
13      // TODO Do something with the image RAW data.
14    }
15 };
16  
17 PictureCallback jpegCallback = new PictureCallback() {
18    public void onPictureTaken(byte[] data, Camera camera) {
19      // Save the image JPEG data to the SD card
20      FileOutputStream outStream = null;
21      try {
22        String path = Environment.getExternalStorageDirectory() +
23                          “\test.jpg”;
24  
25        outStream = new FileOutputStream(path);
26        outStream.write(data);
27        outStream.close();
28      } catch (FileNotFoundException e) {
29        Log.e(TAG, “File Note Found”, e);
30      } catch (IOException e) {
31        Log.e(TAG, “IO Exception”, e);
32      }
33    }
34 };

android之多媒体篇(二)的更多相关文章

  1. android之多媒体篇(一)

    Android 4.0.3(Api Level 15)支持的多媒体格式. 注意:有些设备可能支持其他的文件格式. 1.Audio AAC LC/LTP.HE-AACv1(AAC+).AMR-NB.AM ...

  2. android之多媒体篇(三)

    录像 Android提供了2种方案去录像. 方案一: 最简单的方式就是使用Intents去启动App来帮助你完成.这个方案使你能够指定输出的位置和视频的质量.这方案通常是最好的方法,应该可以用在多种情 ...

  3. 【转】android 电容屏(二):驱动调试之基本概念篇

    关键词:android  电容屏 tp 工作队列 中断 多点触摸协议平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310(samsung ...

  4. Android APP压力测试(二)之Monkey信息自动收集脚本

      Android APP压力测试(二) 之Monkey信息自动收集脚本 前言: 上一篇Monkey介绍基本搬抄官方介绍,主要是为了自己查阅方便.本文重点介绍我在进行Monkey时如何自动收集相关信息 ...

  5. Android项目实战(二十八):Zxing二维码实现及优化

    前言: 多年之前接触过zxing实现二维码,没想到今日项目中再此使用竟然使用的还是zxing,百度之,竟是如此牛的玩意. 当然,项目中我们也许只会用到二维码的扫描和生成两个功能,所以不必下载完整的ja ...

  6. Android多线程分析之二:Thread的实现

    Android多线程分析之二:Thread的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处   在前文<Android多线程分析之一 ...

  7. Android中插件开发篇之----动态加载Activity(免安装运行程序)

    一.前言 又到周末了,时间过的很快,今天我们来看一下Android中插件开发篇的最后一篇文章的内容:动态加载Activity(免安装运行程序),在上一篇文章中说道了,如何动态加载资源(应用换肤原理解析 ...

  8. Android Studio系列教程二--基本设置与运行

    Android Studio系列教程二--基本设置与运行 2014 年 11 月 28 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处! 上面一篇博客,介绍了Studio的 ...

  9. Android高手进阶教程(二十八)之---Android ViewPager控件的使用(基于ViewPager的横向相册)!!!

      分类: Android高手进阶 Android基础教程 2012-09-14 18:10 29759人阅读 评论(35) 收藏 举报 android相册layoutobjectclassloade ...

随机推荐

  1. leetcode:Integer to Roman(整数转化为罗马数字)

    Question: Given an integer, convert it to a roman numeral. Input is guaranteed to be within the rang ...

  2. extern "c" 的作用

    作用:实现C和C++混合编程. 原理:C和C++编译器编译之后,函数名会编译成不同的名字,链接阶段名字查找会找不到目标,后面实例中会详解. 用法:①.c文件中定义的函数,.cpp文件要调用时,该.cp ...

  3. Python:字符串

    一.序列的概念 序列是容器类型,顾名思义,可以想象,“成员”们站成了有序的队列,我们从0开始进行对每个成员进行标记,0,1,2,3,...,这样,便可以通过下标访问序列的一个或几个成员,就像C语言中的 ...

  4. 如何创建Asp.net MVC ViewModel

    ASP.NET MVC View Model Patterns Since MVC has been released I have observed much confusion about how ...

  5. hadoop2.5.2学习及实践笔记(二)—— 编译源代码及导入源码至eclipse

    生产环境中hadoop一般会选择64位版本,官方下载的hadoop安装包中的native库是32位的,因此运行64位版本时,需要自己编译64位的native库,并替换掉自带native库. 源码包下的 ...

  6. POJ 2395 Out of Hay(MST)

    [题目链接]http://poj.org/problem?id=2395 [解题思路]找最小生成树中权值最大的那条边输出,模板过的,出现了几个问题,开的数据不够大导致运行错误,第一次用模板,理解得不够 ...

  7. 麒麟OS剽窃

    今年对于我们的IT行业来说可以算是耻辱的一年. 首先是“汉芯丑闻”,上海交大研制了一个所谓的国内第一个完全拥有自主知识产 权的DSP芯片(数字信号微处理器)——“汉芯”,研制人陈进教授以此领取政府一亿 ...

  8. Kali Linux 安装教程-转

    rootoorotor昨天折腾了 Kali Linux 1.0,把大概的配置过程记录下来,希望对想接触或使用Kali Linux的同学有所帮助.   请注意: 1.本文为面向新手的教程,没技术含量,没 ...

  9. vs常用插件之javsscript插件

    1.JSEnhancements 折叠JS和CSS代码 http://visualstudiogallery.msdn.microsoft.com/0696ad60-1c68-4b2a-9646-4b ...

  10. commondline 之二 执行类

    E:\cn\zno\commandline\Test.class package cn.zno.commandline; import java.lang.management.ManagementF ...