Android 音视频开发(一)

Camera(相机)

图像采集流程

  • 1、获取权限

	<!-- 相机 -->
	<uses-permission android:name="android.permission.CAMERA" /> 

  • 2、构建预览画布 -TextureView/SurfaceView

    //直接创建一个TextureView 并添加到LinearLayout 布局中,注意销毁的时候清理TextureView 资源的情况下记得remove(TextureView) 
    LinearLayout linearLayout = findViewById(R.id.ll_texture_view);
    TextureView textureView =  new TextureView(this);
    LinearLayout.LayoutParams layoutParams =new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.MATCH_PARENT);
    textureView.setLayoutParams(layoutParams);
    linearLayout.addView(textureView);

  • 3、打开相机 -Camera.open
    //默认情况下:相机打开的是后置摄像头
    Camera.open();

  • 4.设置摄像机参数 -Parameters
    通过 Camera.getParameters()获取Parameters
参数作用
setPreviewFormat / setPictureFormat设置预览数据格式(默认是NV21)
setPreviewSize / setPictureSize设置摄像头宽、高
setFocusMod设置对焦模式
getSupportedPreviewSizes / getSupportedPictureSizes获取摄像头支持的预览大小
  • 5、设置预览画布并启动 -startPreview/setPreviewTexture
      //通过textureView.setSurfaceTextureListener()获取surfaceTexture
      mCamera.setPreviewTexture(surfaceTexture);
      mCamera.startPreview();
  • 7、设置预览数据回调 -PreviewCallback
    /**
     * 预览帧显示
     * @param bytes 预览帧数据(NV21格式)
     * @param camera 相机
    */
    @Override
    public void onPreviewFrame(byte[] bytes, Camera camera) {

    }
  • 7、释放相机 -stopPreview/release/remove

   //预览数据回调接口
   mCamera.setPreviewCallback(null);
   //停止预览
   mCamera.stopPreview();
   //释放摄像头
   mCamera.release();
   mCamera = null;
   //移除textureView 画布
   linearLayout.remove(textureView)

AudioRecord(录音)

录音流程:

  • 1、获取权限
	 <!-- 录音权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
        <!-- 读写权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
  • 2、初始化AudioRecord
	
   private void initRecord() {
   		//通过 AudioRecord.getMinBufferSize获取当前配置下最小的缓存大小
        minBufferSize = AudioRecord.getMinBufferSize(
                44100,
                AudioFormat.CHANNEL_IN_STEREO
                , AudioFormat.ENCODING_PCM_16BIT);

        audioRecord = new AudioRecord(
                MediaRecorder.AudioSource.MIC,
                44100,
                AudioFormat.CHANNEL_IN_STEREO,
                AudioFormat.ENCODING_PCM_16BIT,
                minBufferSize);
		
		//创建线程为了录音保存处理
        HandlerThread handlerThread = new HandlerThread("record");
        handlerThread.start();
        startRecordHandler = new Handler(handlerThread.getLooper());
    }
    
参数作用
audioSource录音源(MediaRecorder.AudioSource.MIC(麦克风))
sampleRateInHz采样率(44100Hz/16000Hz等)
channelConfig音频通道配置(AudioFormat.CHANNEL_IN_STEREO(立体音))
audioFormat返回的数据格式(AudioFormat.ENCODING_PCM_16BIT(pcm格式))
bufferSizeInBytes音数据缓存大小(minBufferSize)
  • 3、开始录制 (保存下来的格式是pcm格式(保存记得在子线程进行))
	//path:保存的位置
 	private void startRecord(String path) {
 		//开始录音
        audioRecord.startRecording();
       	//开始录制
        isRecording = true;
        startRecordHandler.post(new Runnable() {
            @Override
            public void run() {
                FileOutputStream fo = null;
                try {
                    //把缓存放到bytes
                    byte[] bytes = new byte[minBufferSize];
                    //创建文件
                    fo = new FileOutputStream(path);
                    //保持录制直到isRecording=false=stop
                    while (isRecording) {
                        //一直读取缓存的数据
                        int read = audioRecord.read(bytes, 0, bytes.length);
                        if (read > 0) {
                            //写入保存的文件当中
                            fo.write(bytes, 0, bytes.length);
                        }
                    }
                    //释放资源
                    fo.close();
                } catch (Exception e) {
                     e.fillInStackTrace();
                } finally {
                    if (fo != null) {
                        try {
                           //释放资源
                            fo.close();
                        } catch (IOException e) {
                              e.fillInStackTrace();                        }
                    }
                }
            }
        });
    }
 	
  • 4、停止录制和清理资源
	//暂停
 	private void stopRecord() {
        isRecording = false;
        closeRecord();
        //pcm数据转换wav
        pcmToWav(path, pathWav, 44100);
   	}
    
    private void closeRecord() {
        if (audioRecord == null) return;
        //判断是否初始失败
        if (audioRecord.getState() == AudioRecord.STATE_UNINITIALIZED) {
            Log.e(TAG, "AudioRecord initialize fail !");
            return;
        }
        //暂停录制
        audioRecord.stop();
        //释放audioRecord资源
        audioRecord.release();
        //退出线程
        handlerThread.quit();
    }
  • 5、数据转换
    /**
     * pcm格式转换成wav数据
     */
    private void pcmToWav(final String pcmFileName, final String wavFileName, long sampleRateInHz) {
        FileInputStream fis;
        FileOutputStream fos;
        long fiSize;
        long fiSize36;
        //根据音频通道配置(AudioFormat.CHANNEL_IN_STEREO=2?1)
        int channels = 2;
        long byteRate = 16 * sampleRateInHz * channels / 8;
        byte[] bytes = new byte[minBufferSize];

        try {
            fis = new FileInputStream(pcmFileName);
            fos = new FileOutputStream(wavFileName);
            fiSize = fis.getChannel().size();
            fiSize36 = fiSize + 36;
            writeWavFileHeader(fos, fiSize, fiSize36, sampleRateInHz, channels, byteRate);
            //一直写入.wav文件,直到.pcm文件读完所有字节
            while (fis.read(bytes) != -1) {
                fos.write(bytes, 0, bytes.length);
            }
            //资源释放
            fis.close();
            fos.close();
        } catch (Exception e) {
            e.fillInStackTrace();
        }
    }

    /**
     * 在wav文件头添加内容(在头部添加44个字节)
     */
    private void writeWavFileHeader(FileOutputStream fos,
                                    long fiSize, long fiSize36, long sampleRate,
                                    long channels, long byteRate) throws Exception {
        byte[] header = new byte[44];
        header[0] = 'R';
        header[1] = 'I';
        header[2] = 'F';
        header[3] = 'F';
        header[4] = (byte) (fiSize36 & 0xff);
        header[5] = (byte) ((fiSize36 >> 8) & 0xff);
        header[6] = (byte) ((fiSize36 >> 16) & 0xff);
        header[7] = (byte) ((fiSize36 >> 24) & 0xff);
        header[8] = 'W'; //WAVE
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';
        header[12] = 'f'; // 'fmt ' chunk
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';
        header[16] = 16; // 4 bytes: size of 'fmt ' chunk
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        header[20] = 1; // format = 1
        header[21] = 0;
        header[22] = (byte) channels;
        header[23] = 0;
        header[24] = (byte) (sampleRate & 0xff);
        header[25] = (byte) ((sampleRate >> 8) & 0xff);
        header[26] = (byte) ((sampleRate >> 16) & 0xff);
        header[27] = (byte) ((sampleRate >> 24) & 0xff);
        header[28] = (byte) (byteRate & 0xff);
        header[29] = (byte) ((byteRate >> 8) & 0xff);
        header[30] = (byte) ((byteRate >> 16) & 0xff);
        header[31] = (byte) ((byteRate >> 24) & 0xff);
        header[32] = (byte) (2 * 16 / 8); // block align
        header[33] = 0;
        header[34] = 16; // bits per sample
        header[35] = 0;
        header[36] = 'd'; //data
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        header[40] = (byte) (fiSize & 0xff);
        header[41] = (byte) ((fiSize >> 8) & 0xff);
        header[42] = (byte) ((fiSize >> 16) & 0xff);
        header[43] = (byte) ((fiSize >> 24) & 0xff);
        fos.write(header, 0, 44);
    }

MediaFormat(参数配置(色彩配置(初始化是黑白色)))

使用

  • 1、初始化MediaFormat

   //MediaFormat.MIMETYPE_VIDEO_AVC:H_264
   //MediaFormat.MIMETYPE_VIDEO_HEVC:H_265
   MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
 
  • 2、参数设置

   //色彩空间
   format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
    //码率
   format.setInteger(MediaFormat.KEY_BIT_RATE, 500_000);
   //帧率
   format.setInteger(MediaFormat.KEY_FRAME_RATE, 20);
   //关键帧
   format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2);
   

MediaCodec(解码器(NV21->i420->mp4))

使用

  • 1、创建解码器

   //创建编码器
   mCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
   //将参数配置(MediaFormat)关联起来
   mCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
  • 2、开始编码
 mCodec.start();

MediaMuxer (混合器(将视频+音频合并起来的容器))

使用

  • 1、创建混合器
	//输出格式为 .mp4
    mMuxer = new MediaMuxer(path, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

总结:这一篇到这里就结束了,谢谢观看,想必您对音视频开发有一定的了解了吧,这一章主要是让您了解一下音频开发在Android原生中需要用到SDK中的哪一些API,接下来就是对每一个API的源码分析以及延申技术的讲解了,期待一下吧!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值