Anroid音视频开发

先简要说明下Android支持的视频格式,如下:

Anroid音视频开发
由于硬件的不同,不同的手机支持的编解码方式有些不一样,在T-Mobile G1实际设备中增加了对WMA,WMV,H.264  AVC格式解码的支持。Android支持的音/视频编码方式仅包括:AMR-NB,H.263,输出的视频格式也只*.3gp或者*.mp4,这点在以后的开发中需要注意。

对Android的编解码有了一些了解后,我们再去研究下如何在Android上来播放/录制视频,打开Android SDK中关于媒体方面的说明,摘要其主要部分如下:
Anroid音视频开发
首先注意的就是:MediaPlayer,MediaRecoder,主要是用来播放视频与录制视频的类;由于这2个类都比较复杂,本篇在后面会详细介绍如何使用MediaRecoder,MediaPlayer将在以后篇幅中再详细介绍。
其他的几个类,定义Android支持的编码方式,如下:

说明 编码方式定义
MediaRecorder.AudioEncoder 音频编码当时 AMR_NB:AMR NB编码
MediaRecorder.AudioSource 声音采样设备 MIC:麦克风
MediaRecorder.OutputFormat 录制输出格式 MPEG_4:*.mp4
RAW_AMR:*.amr
THREE_GPP:*.3gp
MediaRecorder.VideoEncoder 视频编码方式 H263:H.263编码
H264:H.264编码
MPEG_4_SP:mp4编码
MediaRecorder.VideoSource 视频采样设备 CAMERA:数码相机
如何使用MediaRecorder录制声音?
先对声音录制有个大致的了解,需要 设置声音 数据的来源、输出编码方式、输出 文件路径、输出文件格式等。有一点需要说明的是:输出文件格式就相当于一个容器,具体采用什么编码需要指定编码格式。编码一样可能输出格式不一样,输出格式一样其编码方式可能不一样。
android.media 包含与媒体子 系统交互的类。使用 android.media.MediaRecorder 类进行媒体采样,包括音频和视频。MediaRecorder 作为状态机 运行。需要设置不同的参数,比如源设备和格式。设置后,可执行任何 时间长度的录制,直到 用户停止。
录制音频主要片段如下:
MediaRecorder mrec ;
File audiofile = null;
private static final String TAG=”SoundRecordingDemo”;
protected void startRecording() throws IOException
{
mrec.setAudioSource(MediaRecorder.AudioSource.MIC);
mrec.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mrec.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
if (mSampleFile == null)
{
File sampleDir = Environment.getExternalStorageDirectory();
try
{
audiofile = File.createTempFile(”ibm”, “.3gp”, sampleDir);
}catch (IOException e)
{
Log.e(TAG,”sdcard access error”);
return;
}
}
mrec.setOutputFile(audiofile.getAbsolutePath());
mrec.prepare();
mrec.start();
}
protected void stopRecording()
{
mrec.stop();
mrec.release();
processaudiofile(audiofile.getAbsolutePath());
}
protected void processAudiofile()
{
ContentValues values = new ContentValues(3);
long current = System.currentTimeMillis();
values.put(MediaStore.Audio.Media.TITLE, “audio” + audiofile.getName());
values.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000));
values.put(MediaStore.Audio.Media.MIME_TYPE, “audio/3gpp”);
values.put(MediaStore.Audio.Media.DATA, audiofile.getAbsolutePath());
ContentResolver contentResolver = getContentResolver();
Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Uri newUri = contentResolver.insert(base, values);
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, newUri));
}
对以上 代码解析如下:
  • 在 startRecording 方法中,实例化并初始化 MediaRecorder 的实例。
  • 输入源被设置为麦克风(MIC)。
  • 输出格式被设置为 3GPP(*.3gp 文件),这是移动设备专用的媒体格式。
  • 编码器被设置为 AMR_NB,这是音频格式,采样率为 8 KHz。NB 表示窄频。SDK 文档 解释了不同的数据格式和可用的编码器。
  • 音频文件存储在存储卡而不是内存中。External.getExternalStorageDirectory() 返回存储卡位置的名称,在该目录中将创建一个临时文件名。然后,通过调用 setOutputFile 方法将文件关联到 MediaRecorder 实例。音频数据将存储到该文件中。
  • 调用 prepare 方法完成 MediaRecorder 的初始化。准备开始录制流程时,将调用 start 方法。在调用 stop 方法之前,将对存储卡上的文件进行录制。release 方法将释放分配给 MediaRecorder 实例的资源。
  • 音频采样完成之后, 向设备的媒体库添加该音频。在该代码样例中,processAudiofile 方法将音频添加到媒体库。使用 Intent 通知设备上的媒体应用程序有新内容可用。
  • 最后还需要注意的是,录制声音需要一定的权限,需要向 AndroidManifest.xml 添加权限声明:


<uses-permission android:name=”android.permission.RECORD_AUDIO”></uses-permission>总结说明:

  • Android SDK中对视频开发方面提供了很大的方便,但是在使用的过程中发现,这些类封装的都很高层,很多地方估计也无法满足开发人员的需求,主要体现在缺少对文件流操作等底层接口,我们只能通过控制状态来控制文件流,缺少这方面操作增加了Android视频开发的难度,比如说增加其他编码器等。
  • Android T-Mobile G1可以在线看Youtube视频,然而我们在开发中却发现,其无法播放FLV格式的视频,这就有些不明白了?估计在未来应该会支持的,FLV是当前最流行的流媒体格式,如果不支持对用户在Android上直接看这些FLV视频会有很大的影响。

来自:http://www.360doc.com/content/10/1214/17/474846_78089534.shtml#

 

android音频eq平衡http://www.j2megame.com/html/xwzx/ty/1519.html

Android 音频均衡器,调节音频EQ

2.3之后,google 为Mediaplayer 类添加了EQ支持,如果你需要使用这个EQ功能,有两点需要注意,分别为如下:

在配置文件中设置最小的SDK版本为9
添加权限Android.permission.RECORD_AUDIO 即可使用,具体的使用方法,下文会介绍
为了使应用程序能够支持波段变化,我们需要重新定义一个VIEW对象,在onDraw 方法画频谱,代码如下:

 

class VisualizerView extends View {

        private byte[] mBytes;
        private float[] mPoints;
        // 矩形区域
        private Rect mRect = new Rect();
        // 画笔
        private Paint mPaint = new Paint();

        // 初始化画笔
        private void init() {
            mBytes = null;
            mPaint.setStrokeWidth(1f);
            mPaint.setAntiAlias(true);
            mPaint.setColor(Color.BLUE);
        }

        public VisualizerView(Context context) {
            super(context);
            init();
        }

        public void updateVisualizer(byte[] mbyte) {
            mBytes = mbyte;
            invalidate();
        }

        @Override
        protected void onDraw(Canvas canvas) {
            // TODO Auto-generated method stub
            super.onDraw(canvas);

            if (mBytes == null) {
                return;
            }
            if (mPoints == null || mPoints.length < mBytes.length * 4) {
                mPoints = new float[mBytes.length * 4];
            }

            mRect.set(0, 0, getWidth(), getHeight());

            for (int i = 0; i < mBytes.length - 1; i++) {
                mPoints[i * 4] = mRect.width() * i / (mBytes.length - 1);
                mPoints[i * 4 + 1] = mRect.height() / 2
                        + ((byte) (mBytes[i] + 128)) * (mRect.height() / 2)
                        / 128;
                mPoints[i * 4 + 2] = mRect.width() * (i + 1)
                        / (mBytes.length - 1);
                mPoints[i * 4 + 3] = mRect.height() / 2
                        + ((byte) (mBytes[i + 1] + 128)) * (mRect.height() / 2)
                        / 128;
            }

            canvas.drawLines(mPoints, mPaint);
        }
    }
 

 

另外,为了使用EQ和频谱可视化,我们必须了解以下两个类:

Visualizer
此类能使应用程序获取当前有效的一部分音频可视化的目的。使用此类必须添加上面提到的权限。
Equalizer
一个均衡器的类,使用此类可以轻松的操纵音频的频段,和输出的混合 。
具体使用代码和注释见下面:

 

/**
     * 通过mMediaPlayer返回的AudioSessionId创建一个优先级为0均衡器对象 并且通过频谱生成相应的UI和对应的事件
     */
    private void setupEqualizeFxAndUi() {
        mEqualizer = new Equalizer(0, mMediaPlayer.getAudioSessionId());
        mEqualizer.setEnabled(true);// 启用均衡器
        TextView eqTextView = new TextView(this);
        eqTextView.setText("均衡器:");
        mLayout.addView(eqTextView);

        // 通过均衡器得到其支持的频谱引擎
        short bands = mEqualizer.getNumberOfBands();

        // getBandLevelRange 是一个数组,返回一组频谱等级数组,
        // 第一个下标为最低的限度范围
        // 第二个下标为最大的上限,依次取出
        final short minEqualizer = mEqualizer.getBandLevelRange()[0];
        final short maxEqualizer = mEqualizer.getBandLevelRange()[1];

        for (short i = 0; i < bands; i++) {
            final short band = i;

            TextView freqTextView = new TextView(this);
            freqTextView.setLayoutParams(new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.FILL_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT));

            freqTextView.setGravity(Gravity.CENTER_HORIZONTAL);

            // 取出中心频率
            freqTextView
                    .setText((mEqualizer.getCenterFreq(band) / 1000) + "HZ");
            mLayout.addView(freqTextView);

            LinearLayout row = new LinearLayout(this);
            row.setOrientation(LinearLayout.HORIZONTAL);

            TextView minDbTextView = new TextView(this);
            minDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT));

            minDbTextView.setText((minEqualizer / 100) + " dB");

            TextView maxDbTextView = new TextView(this);
            maxDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT));
            maxDbTextView.setText((maxEqualizer / 100) + " dB");

            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.FILL_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);

            layoutParams.weight = 1;

            SeekBar seekbar = new SeekBar(this);
            seekbar.setLayoutParams(layoutParams);
            seekbar.setMax(maxEqualizer - minEqualizer);
            seekbar.setProgress(mEqualizer.getBandLevel(band));

            seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
                }

                @Override
                public void onProgressChanged(SeekBar seekBar, int progress,
                        boolean fromUser) {
                    // TODO Auto-generated method stub
                    mEqualizer.setBandLevel(band,
                            (short) (progress + minEqualizer));
                }
            });
            row.addView(minDbTextView);
            row.addView(seekbar);
            row.addView(maxDbTextView);

            mLayout.addView(row);
        }

    }
 
 

 

 

 

/**
     * 生成一个VisualizerView对象,使音频频谱的波段能够反映到 VisualizerView上
     */
    private void setupVisualizerFxAndUi() {
        mVisualizerView = new VisualizerView(this);
        mVisualizerView.setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.FILL_PARENT,
                (int) (VISUALIZER_HEIGHT_DIP * getResources()
                        .getDisplayMetrics().density)));
        mLayout.addView(mVisualizerView);

        mVisualizer = new Visualizer(mMediaPlayer.getAudioSessionId());
        // 参数内必须是2的位数
        mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);

        // 设置允许波形表示,并且捕获它
        mVisualizer.setDataCaptureListener(new OnDataCaptureListener() {

            @Override
            public void onWaveFormDataCapture(Visualizer visualizer,
                    byte[] waveform, int samplingRate) {
                // TODO Auto-generated method stub
                mVisualizerView.updateVisualizer(waveform);
            }

            @Override
            public void onFftDataCapture(Visualizer visualizer, byte[] fft,
                    int samplingRate) {
                // TODO Auto-generated method stub

            }
        }, Visualizer.getMaxCaptureRate() / 2, true, false);

    }

 

 

 

进入程序后,在程序入口加载如下代码:

 

mStatusTextView = new TextView(this);
        mLayout = new LinearLayout(this);
        mLayout.setOrientation(LinearLayout.VERTICAL);
        mLayout.addView(mStatusTextView);
        setContentView(mLayout);

        mMediaPlayer = MediaPlayer.create(this, R.raw.eason);

        setupVisualizerFxAndUi();
        setupEqualizeFxAndUi();

        mVisualizer.setEnabled(true);
        mMediaPlayer.setOnCompletionListener(new OnCompletionListener() {

            @Override
            public void onCompletion(MediaPlayer mp) {
                // TODO Auto-generated method stub
                mVisualizer.setEnabled(false);
            }
        });

        mMediaPlayer.start();
        mStatusTextView.setText("播放中。。。");
 

 

2.3之后,google 为Mediaplayer 类添加了EQ支持,如果你需要使用这个EQ功能,有两点需要注意,分别为如下:

在配置文件中设置最小的SDK版本为9
添加权限Android.permission.RECORD_AUDIO 即可使用,具体的使用方法,下文会介绍
为了使应用程序能够支持波段变化,我们需要重新定义一个VIEW对象,在onDraw 方法画频谱,代码如下:

 

class VisualizerView extends View {

        private byte[] mBytes;
        private float[] mPoints;
        // 矩形区域
        private Rect mRect = new Rect();
        // 画笔
        private Paint mPaint = new Paint();

        // 初始化画笔
        private void init() {
            mBytes = null;
            mPaint.setStrokeWidth(1f);
            mPaint.setAntiAlias(true);
            mPaint.setColor(Color.BLUE);
        }

        public VisualizerView(Context context) {
            super(context);
            init();
        }

        public void updateVisualizer(byte[] mbyte) {
            mBytes = mbyte;
            invalidate();
        }

        @Override
        protected void onDraw(Canvas canvas) {
            // TODO Auto-generated method stub
            super.onDraw(canvas);

            if (mBytes == null) {
                return;
            }
            if (mPoints == null || mPoints.length < mBytes.length * 4) {
                mPoints = new float[mBytes.length * 4];
            }

            mRect.set(0, 0, getWidth(), getHeight());

            for (int i = 0; i < mBytes.length - 1; i++) {
                mPoints[i * 4] = mRect.width() * i / (mBytes.length - 1);
                mPoints[i * 4 + 1] = mRect.height() / 2
                        + ((byte) (mBytes[i] + 128)) * (mRect.height() / 2)
                        / 128;
                mPoints[i * 4 + 2] = mRect.width() * (i + 1)
                        / (mBytes.length - 1);
                mPoints[i * 4 + 3] = mRect.height() / 2
                        + ((byte) (mBytes[i + 1] + 128)) * (mRect.height() / 2)
                        / 128;
            }

            canvas.drawLines(mPoints, mPaint);
        }
    }
 

 

另外,为了使用EQ和频谱可视化,我们必须了解以下两个类:

Visualizer
此类能使应用程序获取当前有效的一部分音频可视化的目的。使用此类必须添加上面提到的权限。
Equalizer
一个均衡器的类,使用此类可以轻松的操纵音频的频段,和输出的混合 。
具体使用代码和注释见下面:

 

/**
     * 通过mMediaPlayer返回的AudioSessionId创建一个优先级为0均衡器对象 并且通过频谱生成相应的UI和对应的事件
     */
    private void setupEqualizeFxAndUi() {
        mEqualizer = new Equalizer(0, mMediaPlayer.getAudioSessionId());
        mEqualizer.setEnabled(true);// 启用均衡器
        TextView eqTextView = new TextView(this);
        eqTextView.setText("均衡器:");
        mLayout.addView(eqTextView);

        // 通过均衡器得到其支持的频谱引擎
        short bands = mEqualizer.getNumberOfBands();

        // getBandLevelRange 是一个数组,返回一组频谱等级数组,
        // 第一个下标为最低的限度范围
        // 第二个下标为最大的上限,依次取出
        final short minEqualizer = mEqualizer.getBandLevelRange()[0];
        final short maxEqualizer = mEqualizer.getBandLevelRange()[1];

        for (short i = 0; i < bands; i++) {
            final short band = i;

            TextView freqTextView = new TextView(this);
            freqTextView.setLayoutParams(new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.FILL_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT));

            freqTextView.setGravity(Gravity.CENTER_HORIZONTAL);

            // 取出中心频率
            freqTextView
                    .setText((mEqualizer.getCenterFreq(band) / 1000) + "HZ");
            mLayout.addView(freqTextView);

            LinearLayout row = new LinearLayout(this);
            row.setOrientation(LinearLayout.HORIZONTAL);

            TextView minDbTextView = new TextView(this);
            minDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT));

            minDbTextView.setText((minEqualizer / 100) + " dB");

            TextView maxDbTextView = new TextView(this);
            maxDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT));
            maxDbTextView.setText((maxEqualizer / 100) + " dB");

            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.FILL_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);

            layoutParams.weight = 1;

            SeekBar seekbar = new SeekBar(this);
            seekbar.setLayoutParams(layoutParams);
            seekbar.setMax(maxEqualizer - minEqualizer);
            seekbar.setProgress(mEqualizer.getBandLevel(band));

            seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
                }

                @Override
                public void onProgressChanged(SeekBar seekBar, int progress,
                        boolean fromUser) {
                    // TODO Auto-generated method stub
                    mEqualizer.setBandLevel(band,
                            (short) (progress + minEqualizer));
                }
            });
            row.addView(minDbTextView);
            row.addView(seekbar);
            row.addView(maxDbTextView);

            mLayout.addView(row);
        }

    }
 
 

 

 

 

/**
     * 生成一个VisualizerView对象,使音频频谱的波段能够反映到 VisualizerView上
     */
    private void setupVisualizerFxAndUi() {
        mVisualizerView = new VisualizerView(this);
        mVisualizerView.setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.FILL_PARENT,
                (int) (VISUALIZER_HEIGHT_DIP * getResources()
                        .getDisplayMetrics().density)));
        mLayout.addView(mVisualizerView);

        mVisualizer = new Visualizer(mMediaPlayer.getAudioSessionId());
        // 参数内必须是2的位数
        mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);

        // 设置允许波形表示,并且捕获它
        mVisualizer.setDataCaptureListener(new OnDataCaptureListener() {

            @Override
            public void onWaveFormDataCapture(Visualizer visualizer,
                    byte[] waveform, int samplingRate) {
                // TODO Auto-generated method stub
                mVisualizerView.updateVisualizer(waveform);
            }

            @Override
            public void onFftDataCapture(Visualizer visualizer, byte[] fft,
                    int samplingRate) {
                // TODO Auto-generated method stub

            }
        }, Visualizer.getMaxCaptureRate() / 2, true, false);

    }

 

 

 

进入程序后,在程序入口加载如下代码:

 

mStatusTextView = new TextView(this);
        mLayout = new LinearLayout(this);
        mLayout.setOrientation(LinearLayout.VERTICAL);
        mLayout.addView(mStatusTextView);
        setContentView(mLayout);

        mMediaPlayer = MediaPlayer.create(this, R.raw.eason);

        setupVisualizerFxAndUi();
        setupEqualizeFxAndUi();

        mVisualizer.setEnabled(true);
        mMediaPlayer.setOnCompletionListener(new OnCompletionListener() {

            @Override
            public void onCompletion(MediaPlayer mp) {
                // TODO Auto-generated method stub
                mVisualizer.setEnabled(false);
            }
        });

        mMediaPlayer.start();
        mStatusTextView.setText("播放中。。。");

 

Android平台中应该如何获取音乐文件的信息

Android系统提供了MediaScanner,MediaProvider,MediaStore等接口,并且提供了一套数据库表格,通过Content Provider的方式提供给用户。当手机开机或者有SD卡插拔等事件发生时,系统将会自动扫描SD卡和手机内存上的媒体文件,如audio,video,图片等,将相应的信息放到定义好的数据库表格中。在这个程序中,我们不需要关心如何去扫描手机中的文件,只要了解如何查询和使用这些信息就可以了。
MediaStore中定义了一系列的数据表格,通过ContentResolver提供的查询接口,我们可以得到各种需要的信息。下面我们重点介绍查询SD卡上的音乐文件信息。
先来了解一下ContentResolver的查询接口:

        Cursor  query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);  

        Uri:指明要查询的数据库名称加上表的名称,从MediaStore中我们可以找到相应信息的参数,具体请参考 开发文档。
        Projection: 指定查询数据库表中的哪几列,返回的游标中将包括相应的信息。Null则返回所有信息。
        selection: 指定查询条件
        selectionArgs:参数selection里有 ?这个符号是,这里可以以实际值代替这个问号。如果selection这个没有?的话,那么这个String数组可以为null。
        SortOrder:指定查询结果的排列顺序
下面的命令将返回所有在外部存储卡上的音乐文件的信息:
Cursor cursor = query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);  

得到cursor后,我们可以调用Cursor的相关方法具体的音乐信息:

歌曲ID:MediaStore.Audio.Media._ID

Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));  

 

歌曲的名称 :MediaStore.Audio.Media.TITLE

String tilte = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));  

 

歌曲的专辑名:MediaStore.Audio.Media.ALBUM

String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));  


歌曲的歌手名: MediaStore.Audio.Media.ARTIST
String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));  

歌曲文件的路径 :MediaStore.Audio.Media.DATA

String url = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));    

歌曲的总播放时长 :MediaStore.Audio.Media.DURATION

Int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));    

歌曲文件的大小 :MediaStore.Audio.Media.SIZE

Int size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE)); 

 

一些歌词处理的文章:http://blog.csdn.net/dany1202/article/details/6533513

你可能感兴趣的:(anroid)