2023年6月27日发(作者:)
AndroidOpenGLES⼗⼆.MediaCodec录制视频之MediaCodec(转载整理)实现录制⾳视频也有两种⽅案,分别是MediaRecorder和MediaCodec什么是MediaRecorderMediaRecorder是安卓提供的⼀个⽤于⾳视频采集的类MediaRecorder的优缺点优点可以实现直接录制视频 使⽤⽅便,得到就是编码和封装好的⾳视频⽂件,可以直接使⽤缺点⽆法获取原始数据,不能对每⼀帧数据进⾏处理,⽆法⽀持我们程序中⾃⼰需要的⼀些逻辑,⽐⽅需要录制灰度视频。由于不满⾜我的需求,所以这⾥就不再对MediaRecorder讲解了,那接下来我们来说说MediaCodec什么是MediaCodecMediaCodec类是Android平台提供的⽤于访问低层多媒体硬件编/解码器接⼝,它是Android低层多媒体架构的⼀部分,通常与MediaExtractor、MediaMuxer、AudioTrack结合使⽤,能够编解码诸如H.264、H.265、AAC、3gp等常见的⾳视频格式。⼀般来说H.264的AVC视频编码和AAC的⾳频编码是最常见的。MediaCodec⼯作原理MediaCodec的⼯作原理就是处理输⼊数据以产⽣输出数据。具体来说,MediaCodec在编解码的过程中使⽤了⼀组输⼊/输出缓存区来同步或异步处理数据:⾸先,客户端向获取到的编解码器输⼊缓存区写⼊要编解码的数据并将其提交给编解码器,待编解码器处理完毕后将其转存到编码器的输出缓存区,同时收回客户端对输⼊缓存区的所有权;然后,客户端从获取到编解码输出缓存区读取编码好的数据进⾏处理,待处理完毕后编解码器收回客户端对输出缓存区的所有权。不断重复整个过程,直⾄编码器停⽌⼯作或者异常退出。MediaCodec⽣命周期中的状态mediacodec分为三种状态,Stopped,
Executing和Released。⼀张图表⽰(这张图是从⽹上直接下载下来使⽤的):imageStopped状态包含三个⼦状态:Uninitialized,
Configured和Error,Executing同样包含三个状态:Flushed,
Running 和End-of-Stream。在mediacodec的使⽤过程中必须遵守图⾥标出的流程,否则会发⽣错误。⽐⽅,没有调⽤start()⽅法,就开始sotp()会报错。以解码器为例,讲解⼀下使⽤流程。当使⽤⼯⼚⽅法创建mediacodec并且指定为解码后,进⼊Uninitialized状态,调⽤configure⽅法后,进⼊Configured状态,然后调⽤start⽅法进⼊Executing状态。进⼊Executing状态后,⾸先到达Flush状态,此时mediacodec会持有所有的数据,当第⼀个inputbufffer从队列中取出时,⽴即进⼊Running状态,这个时间很短。然后就可以调⽤dequeueInputBuffer和getInputBuffer来获取⽤户可⽤的缓冲区,⽤户填满数据后调⽤queueinputbuffer⽅法返回给解码器,解码器⼤部分时间都会⼯作在Running状态。当想inputbufferqueue中输⼊⼀帧标记EndOfStream的时候,进⼊End-of-Stream状态,在这种状态下,解码器不再接受任何新的数据输⼊,缓冲区中的数据和标记EndOfStream最终会执⾏完毕。在任何时候都可以调⽤flush⽅法回到Flush状态。调⽤stop⽅法会使mediacode进⼊
Uninitialized状态,这时候可以执⾏configure⽅法来进⼊下⼀循环。当mediacodec使⽤完毕后必须调⽤release⽅法来释放所有的资源。在某些情况下,例如取出缓冲区索引时,mediacodec会发⽣错误进⼊Error状态,此时调⽤reset⽅法来是mediacodec重新处于Uninitialized状态,或者调⽤release来结束解码。MediaCodec API 说明MediaCodec 主要的API做⼀个介绍:MediaCodec创建:createDecoderByType/createEncoderByType:根据特定MIME类型(如"video/avc")创建codec。createByCodecName:知道组件的确切名称(如r)的时候,根据组件名创建codec。使⽤MediaCodecList可以获取组件的名称。configure:配置解码器或者编码器。start:成功配置组件后调⽤start。buffer处理的接⼝:dequeueInputBuffer:从输⼊流队列中取数据进⾏编码操作。queueInputBuffer:输⼊流⼊队列。dequeueOutputBuffer:从输出队列中取出编码操作之后的数据。releaseOutputBuffer:处理完成,释放ByteBuffer数据。getInputBuffers:获取需要编码数据的输⼊流队列,返回的是⼀个ByteBuffer数组。getOutputBuffers:获取编解码之后的数据输出流队列,返回的是⼀个ByteBuffer数组。flush:清空的输⼊和输出端⼝。stop:终⽌decode/encode会话release:释放编解码器实例使⽤的资源。MediaCodec创建编/解码器MediaCodec主要提供了createEncoderByType(String type)、createDecoderByType(String type)两个⽅法来创建编解码器,它们均需要传⼊⼀个MIME类型多媒体格式。常见的MIME类型多媒体格式如下:● “video/8” - VP8 video (i.e. video in .webm)● “video/9” - VP9 video (i.e. video in .webm)●
“video/avc” - H.264/AVC video● “video/mp4v-es” - MPEG4 video● “video/3gpp” - H.263 video● “audio/3gpp” - AMR narrowband audio● “audio/amr-wb” - AMR wideband audio● “audio/mpeg” - MPEG1/2 audio layer III● “audio/mp4a-latm” - AAC audio (note, this is raw AAC packets, not packaged in LATM!)● “audio/vorbis” - vorbis audio● “audio/g711-alaw” - G.711 alaw audio● “audio/g711-mlaw” - G.711 ulaw audioMediaCodec参数配置private void initVideoCodec(int width, int height) { try { // /reference/android/media/MediaCodec mediacodec官⽅介绍 // ⽐⽅MediaCodec的⼏种状态 // avc即h264编码 MediaFormat mediaFormat = VideoFormat(PE_VIDEO_AVC,width,height); // 设置颜⾊格式 // 本地原始视频格式(native raw video format):这种格式通过COLOR_FormatSurface标记,并可以与输⼊或输出Surface⼀起使⽤ eger(_COLOR_FORMAT, _FormatSurface); // 设置码率,通常码率越⾼,视频越清晰,但是对应的视频也越⼤ eger(_BIT_RATE,width * height * 4); // 设置帧率 三星s21⼿机camera预览时,⽀持的帧率为10-30 // 通常这个值越⾼,视频会显得越流畅,⼀般默认设置成30,你最低可以设置成24,不要低于这个值,低于24会明显卡顿,微信为28 eger(_FRAME_RATE,30); // 设置 I 帧间隔的时间 // 通常的⽅案是设置为 1s,对于图⽚电影等等特殊情况,这⾥可以设置为 0,表⽰希望每⼀帧都是 KeyFrame // IFRAME_INTERVAL是指的帧间隔,这是个很有意思的值,它指的是,关键帧的间隔时间。通常情况下,你设置成多少问题都不⼤。 // ⽐如你设置成10,那就是10秒⼀个关键帧。但是,如果你有需求要做视频的预览,那你最好设置成1 // 因为如果你设置成10,那你会发现,10秒内的预览都是⼀个截图 eger(_I_FRAME_INTERVAL,5); // 创建编码器 // /a/ MediaCodec 退坑指南 mMediaCodec = EncoderByType(PE_VIDEO_AVC); ure(mediaFormat,null,null,URE_FLAG_ENCODE); // 相机的像素数据绘制到该 surface 上⾯ mSurface = InputSurface(); videoEncoderThread = new VideoEncoderThread(videoRecorderReference); } catch (Exception e) { tackTrace(); }}可以⽤微信录制⼀个短视频,然后看下参数:⽐较有参照意思的参数://录制了4.917秒Duration/String : 4 秒 917 毫秒//每秒⼀帧Format_Settings_RefFrames/String : 4 帧//视频宽⾼Width/String : 288 像素Height/String : 640 像素//1601*1024除以288*640=8.9,KEY_BIT_RATE和宽⾼⽐接近9OverallBitRate/String : 1 601 kb/s//格式avc,和MIMETYPE_VIDEO_AVC对应Format/String : AVC"FileExtension" : "mp4",//avc也是mpeg-4"Format" : "MPEG-4",//帧率FrameRate/String : 28.067 FPS//yuv420,在camera预览时设定的nv21对应ColorSpace : YUVChromaSubsampling/String : 4:2:0//声⾳的格式,再补充Format/String : AAC LCChannel(s)/String : 1 声道SamplingRate/String : 44.1 kHzFrameRate/String : 43.066 FPS (1024 SPF)configure public void configure( MediaFormat format, Surface surface, MediaCrypto crypto, int flags);MediaFormat format:输⼊数据的格式(解码器)或输出数据的所需格式(编码器)。传null等同于传递MediaFormat#MediaFormat作为空的MediaFormat。Surface surface:指定Surface,⽤于解码器输出的渲染。如果编解码器不⽣成原始视频输出(例如,不是视频解码器)和/或想配置解码器输出ByteBuffer,则传null。MediaCrypto crypto:指定⼀个crypto对象,⽤于对媒体数据进⾏安全解密。对于⾮安全的编解码器,传null。int flags:当组件是编码器时,flags指定为常量CONFIGURE_FLAG_ENCODE。MediaFormat:封装描述媒体数据格式的信息(包括⾳频或视频),以及可选的特性元数据。媒体数据的格式指定为key/value对。key是字符串。值可以integer、long、float、String或ByteBuffer。特性元数据被指定为string/boolean对。开始录制我们通过对MediaCodec参数进⾏配置,然后得到⼀个MediaCodec ();结束录制这⾥需要注意⼀下释放的顺序,⼀定得是按照下⾯的顺序进⾏资源释放的 EndOfInputStream(); (); e();● 下⾯看⼀段源码:当编解码器start后,会进⼊⼀个for(;;)循环,该循环是⼀个死循环,以实现不断地去从编解码器的输⼊缓存池中获取包含数据的⼀个缓存区,然后再从输出缓存池中获取编解码好的输出数据。 MediaCodec codec = ByCodecName(name); ure(format, …); MediaFormat outputFormat = putFormat(); // option B (); for (;;) { int inputBufferId = eInputBuffer(timeoutUs); if (inputBufferId >= 0) { ByteBuffer inputBuffer = utBuffer(…); // fill inputBuffer with valid data … nputBuffer(inputBufferId, …); } int outputBufferId = eOutputBuffer(…); if (outputBufferId >= 0) { ByteBuffer outputBuffer = putBuffer(outputBufferId); MediaFormat bufferFormat = putFormat(outputBufferId); // option A // bufferFormat is identical to outputFormat // outputBuffer is ready to be processed or rendered. … eOutputBuffer(outputBufferId, …); } else if (outputBufferId == _OUTPUT_FORMAT_CHANGED) { // Subsequent data will conform to new format. // Can ignore if using getOutputFormat(outputBufferId) outputFormat = putFormat(); // option B } } (); e();上层数据获取 do { if (mMediaCodec != null) { int outBufferIndex = eOutputBuffer(mBufferInfo, -1); if (outBufferIndex >= 0) { ByteBuffer bb = putBuffer(outBufferIndex); //这⾥获取到原始数据,然后根据相关需求可对数据进⾏处理 } if (outBufferIndex >= 0) { eOutputBuffer(outBufferIndex, false); } } } while (isStarted);
发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1687822701a48210.html
评论列表(0条)