120001 Android视频录制
来源:互联网 发布:阿里云服务器做vpn 编辑:程序博客网 时间:2024/06/03 22:02
本文提供几种可行的在Android端进行录像的方案,对于MP4文件的格式请自行在网上查阅。其中的有些方法经过了个人实践,贴出了核心代码,有一些由于不满足项目需要,没有实践,贴出一些参考资料。
1 常规录制
1.1 场景描述
直接从摄像头和麦克风取数据,经过编码,保存为文件。
1.2 采用方法
这种情况可以直接调用Android的MediaRecorder类。该类获得数据后,通过硬编码,然后保存成文件。操作流程固定。
1.3 核心代码
<span style="white-space:pre"></span>MediaRecorder mMediaRecorder;<span style="white-space:pre"></span>mMediaRecorder=new MediaRecorder(); //设置视频源 mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT); //设置音频源 mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); //设置文件输出格式 mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); //设置视频编码方式 mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); //设置音频编码方式 mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); //设置视频高和宽,注意文档的说明: //Must be called after setVideoSource(). //Call this after setOutFormat() but before prepare(). //设置录制的视频帧率,注意文档的说明: //Must be called after setVideoSource(). //Call this after setOutFormat() but before prepare(). mMediaRecorder.setVideoFrameRate(15); //设置预览画面 mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); //设置输出路径 mMediaRecorder.setOutputFile (Environment.getExternalStorageDirectory()+File.separator+System.currentTimeMillis()+".mp4"); mMediaRecorder.setVideoSize(640, 480); //设置视频的最大持续时间 mMediaRecorder.setMaxDuration(10000); //为MediaRecorder设置监听 mMediaRecorder.setOnInfoListener(new OnInfoListener() { public void onInfo(MediaRecorder mr, int what, int extra) { if (what==MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) { System.out.println("已经达到最长录制时间"); if (mMediaRecorder!=null) { mMediaRecorder.stop(); mMediaRecorder.release(); mMediaRecorder=null; } } } });
2 可修改数据方式进行录像
2.1 场景描述
获取摄像头数据,进行处理,对处理过后视频数据进行编码,同时获得音频数据,进行处理,同样对处理后数据进行编码,将编码后的到数据保存为文件。
2.2 可选方法
(1)对于Android 4.3之后,可以通过MediaCodec和MediaMuxer配合进行录制。处理过后的数据,通过MediaCodec进行编码,然后通过MediaMuxer进行混合。
参考:http://blog.csdn.net/jinzhuojun/article/details/32163149
(2)利用开源库
ffmpeg:http://blog.csdn.net/leixiaohua1020/article/details/15811977这篇博文整理的很好。
http://blog.csdn.net/shaoyizhe2006/article/details/8525738写MP4的例子。
(3)利用MediaCodec库和MP4V2进行录制, MediaCodec只在Android 4.1之后存在。MediaCodec调用硬编码,不占CPU,因此该方法可以保证效率。利用MediaCodec进行编码后,调用封装的MP4V2的写MP4类写成文件。
2.3 方案三细节
2.3.1 MediaCodec的调用
该类主要对数据进行编码,具体操作流程如下:首先对编码器进行初始化,然后在编码器的输入数据队列中添加数据。编码得到的数据从编码器的输出队列中获取。
(1)初始化编码器
<span style="white-space:pre"></span>//video and audio encoderpublic MediaCodec VideoCodecEncoder, AudioCodecEncoder;//video frame propertyint m_FrameWidth = 320; //视频帧宽 <span style="white-space:pre"></span>int m_FrameHeight = 240; //视频帧高<span style="white-space:pre"></span>int m_VideoFrameRate = 20; //视频帧率 <span style="white-space:pre"></span>int m_VideoBitRate = 2000000;//视频比特率<span style="white-space:pre"></span>int m_AudioSamleRate = 22050;//音频采样频率<span style="white-space:pre"></span>int m_AudioChannelCout = 1;//音频通道数<span style="white-space:pre"></span>int m_AudioBitRate = 128000;//音频比特率 <span style="white-space:pre"></span>private int InitEncoder(int width, int height, int videobitrate,int videoframerate, int audiosampleRate, int audiochannelCount, int audiobitrate){VideoCodecEncoder = MediaCodec.createEncoderByType("video/avc"); //"video/avc"为MIME类型,可以查阅支持的类型MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, videobitrate);mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, videoframerate);mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar); mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); //关键帧间隔时间 单位sVideoCodecEncoder.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);VideoCodecEncoder.start();AudioCodecEncoder = MediaCodec.createEncoderByType("audio/mp4a-latm");MediaFormat AudioFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", audiosampleRate, audiochannelCount);
<span style="white-space:pre"></span> //尝试过的手机只支持这种MIME类型AudioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);//测试发现,KEY_BIT_RAT必须设置,否则编码器无法设置成功AudioFormat.setInteger(MediaFormat.KEY_BIT_RATE, audiobitrate);//m.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, mBuffer_Size);AudioCodecEncoder.configure(AudioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);AudioCodecEncoder.start();return 0;}(2)在输入队列中添加数据
<span style="white-space:pre"></span>//write videodata h264//输入视频帧数据格式为YUV420,即YYYYUVUVUVUV//摄像头支持的数据YV12数据排列方式为YYYYUUUUVVVV,需要转换public int WriteVideoData(byte[] YUV420Data){ByteBuffer[] inputBuffers = VideoCodecEncoder.getInputBuffers(); int inputBufferIndex = VideoCodecEncoder.dequeueInputBuffer(-1); if (inputBufferIndex >= 0) { ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; inputBuffer.clear(); inputBuffer.put(YUV420Data); //放入数据 VideoCodecEncoder.queueInputBuffer(inputBufferIndex, 0, YUV420Data.length, 0, 0); } return 0;}//wirte audiodata aac//麦克风数据为PCM格式public int WriteAudioData(byte[] PCMData){ByteBuffer[] AudioinputBuffers = AudioCodecEncoder.getInputBuffers(); int AudioinputBufferIndex = AudioCodecEncoder.dequeueInputBuffer(-1); if (AudioinputBufferIndex >= 0) { ByteBuffer inputBuffer = AudioinputBuffers[AudioinputBufferIndex]; inputBuffer.clear(); inputBuffer.put(PCMData); AudioCodecEncoder.queueInputBuffer(AudioinputBufferIndex, 0, PCMData.length, 0, 0); }return 0;}(3)从输出队列取数据(以视频帧为例)
<span style="white-space:pre"></span>ByteBuffer[] outputBuffers = VideoCodecEncoder.getOutputBuffers();MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = VideoCodecEncoder.dequeueOutputBuffer(bufferInfo,0); int pos = 0; while (outputBufferIndex >= 0) { ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; byte[] outData = new byte[bufferInfo.size]; outputBuffer.get(outData); //取编码后数据 if(m_Video_SPSPPS != null) { System.arraycopy(outData, 0, h264, pos, outData.length); pos += outData.length; } else //保存pps sps 只有开始时 第一个帧里有, 保存起来后面用 //音频第一帧也有信息,一般为2个字节 { ByteBuffer spsPpsBuffer = ByteBuffer.wrap(outData); if (spsPpsBuffer.getInt() == 0x00000001) { m_Video_SPSPPS = new byte[outData.length]; System.arraycopy(outData, 0, m_Video_SPSPPS, 0, outData.length); } else { return -1; } } VideoCodecEncoder.releaseOutputBuffer(outputBufferIndex, false); outputBufferIndex = VideoCodecEncoder.dequeueOutputBuffer(bufferInfo, 0); }//音频不存在关键帧,因此需要添加头信息 byte input[] = new byte[h264.length]; if(h264[4] == 0x65) //key frame 编码器生成关键帧时只有 00 00 00 01 65 没有pps sps, 要加上 { System.arraycopy(h264, 0, input, 0, pos); System.arraycopy(m_Video_SPSPPS, 0, h264, 0, m_Video_SPSPPS.length); System.arraycopy(input, 0, h264, m_Video_SPSPPS.length, pos); pos += m_Video_SPSPPS.length; }
2.3.2 Mp4v2调用
这是一个开源库:以下博文对该库的使用描述比较清楚。
http://blog.csdn.net/sweetloverft/article/details/29851309
包括编译方法,另外如果要在Android上使用,可以针对以下C++版本的调用程序(http://download.csdn.net/detail/nighterll/7592823)中的MP4Encoder类写个jni,然后和android库一起在cgwin下编译成so就可以使用。注意些Android.mk时按网上的写法,在需要编的.h和.cpp后加上MP4Encoder.h和MP4Encoder.cpp,以及自己写的jni的cpp和h文件。值得注意的是mp4v2在编译时cpp和h的编译时有顺序,尝试用通配符的方式(偷懒不想写那么多),出现错误,因此按各种帖子上的方式写,在最好加上自己的东西就可以了。值得一提的是C++版本的这个Demo非常好,已经测试过,很好用,结构清晰,操作简单。作者在CSDN上谦虚的称是封装的较好的,测试确实如此,程序直接可以编译运行,代码结构清晰,看起来不费力。可惜找了半天,找不到下载的地方了,我在上传一份,如果作者看到,请告知我一声,我把链接改到您的位置上。
具体调用过程为:先初始化文件,即建立一个MP4文件;然后写h264的track,需要给的参数就是sps和pps;接下来写AAC的track,给的信息也是一个头信息,似乎大部分都是2个字节;然后写h264的数据;接着写AAC数据;最后关闭文件。中间的步骤,只要对应的track在写数据之前写完,其他的没有什么顺序限制。
C++版本的调用实例:
MP4CreateFile("test.mp4", 5);MP4AddH264Track(buf, len, 640, 480);MP4AddAACTrack(buf, len);MP4WriteH264Data(buf, len, pts);MP4WriteAACData(buf, len, pts);MP4ReleaseFile();
写Data的两个方法可以循环调用,直到写完所有数据。
3 遗留问题
到此,一个可行的方案完成了,测试了录像。存在以下问题:
(1)视频出现马赛克,个人认为是编码器的两个队列处理的问题,由于硬编码速度快,写文件速度慢,而编码器的输出队列只缓存了4个包,前面还没有写完,后面编码输出就覆盖了原来包,导致丢包。不知道理解对不对,希望有高手可以解释。
(2)同步问题,现在给pts是在写数据的时候分别给的PTS,但是同时采集的视频帧处理时间远大于音频的处理时间,如果用采集时间作为PTS,编码器输出的是一个个的包,没有时间信息,不知道怎么做同步。从实际效果来看,现在用写的时间来记录,文件播放似乎也是同步的,但是这始终是个隐患。
遗留的这两个问题希望做相关研究的朋友一起讨论解决方案。
- 120001 Android视频录制
- android视频录制(调用系统视频录制)
- Android 视频录制
- android视频录制
- android 音视频录制
- android视频录制例子
- Android录制视频(五)
- Android录制视频(四)
- Android录制视频(三)
- Android录制视频(二)
- Android录制视频(一)
- Android视频录制
- VLC Android录制视频
- android-音视频录制
- Android MediaRecord视频录制
- android 后台视频录制
- android自定义视频录制
- Android 录制视频
- 段错误等造成死机问题的分析
- 撰写打动人心的功能规范
- 求1+2+…+n,要求不能使用乘除法……
- uPortal安装及部署
- session_start()的逻辑
- 120001 Android视频录制
- Mac系统下配置Sublime Text的lua运行环境
- 避免变量重复定义和重复包含头文件的方法
- VS2010编译器经常遇到的小问题
- TCP/IP 工具收录
- Qt事件循环 跨线程信号和槽
- fragmentActivity跳转不成功
- An internal error occurred during: "Building workspace".
- POJ-2287-Tian Ji -- The Horse Racing