Android短视频系统硬编码—实现音视频编码(三)

MediaMuxer(音视频混合API)

MediaMuxer的使用很简单,在Android Developer官网上MediaMuxer的API说明中,也有其简单的使用示例代码:

MediaMuxer muxer = new MediaMuxer("temp.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4);
// More often, the MediaFormat will be retrieved from MediaCodec.getOutputFormat()
// or MediaExtractor.getTrackFormat().
MediaFormat audioFormat = new MediaFormat(...);
MediaFormat videoFormat = new MediaFormat(...);
int audioTrackIndex = muxer.addTrack(audioFormat);
int videoTrackIndex = muxer.addTrack(videoFormat);
ByteBuffer inputBuffer = ByteBuffer.allocate(bufferSize);
boolean finished = false;
BufferInfo bufferInfo = new BufferInfo();

muxer.start();
while(!finished) {
  // getInputBuffer() will fill the inputBuffer with one frame of encoded
  // sample from either MediaCodec or MediaExtractor, set isAudioSample to
  // true when the sample is audio data, set up all the fields of bufferInfo,
  // and return true if there are no more samples.
  finished = getInputBuffer(inputBuffer, isAudioSample, bufferInfo);
  if (!finished) {
    int currentTrackIndex = isAudioSample ? audioTrackIndex : videoTrackIndex;
    muxer.writeSampleData(currentTrackIndex, inputBuffer, bufferInfo);
  }
};
muxer.stop();
muxer.release();

参照官方的说明和代码示例,我们可以知道,音视频混合(也可以音频和音频混合),只需要将编码器的MediaFormat加入到MediaMuxer中,得到一个音轨视频轨的索引,然后每次从编码器中取出来的ByteBuffer,写入(writeSampleData)到编码器所在的轨道中就ok了。
这里需要注意的是,一定要等编码器设置编码格式完成后,再将它加入到混合器中,编码器编码格式设置完成的标志是dequeueOutputBuffer得到返回值为MediaCodec.INFO_OUTPUT_FORMAT_CHANGED。

音视频录制MP4文件

前面两篇文章已经给出了短视频系统音频录制的代码和视频录制的代码,利用MediaMuxer将其结合起来,就可以和简单的完成录制有声音有图像的MP4文件的功能了。短视频系统音频录制和视频录制的基本流程保持不变,在录制编码后,不再将编码的结果写入到文件流中,而是写入为混合器的sample data。以视频为例,更改循环编码的代码为:

//流程一直,无需更改
int index=mVideoEnc.dequeueInputBuffer(-1);
if(index>=0){
    if(hasNewData){
        if(yuv==null){
            yuv=new byte[width*height*3/2];
        }
        rgbaToYuv(data,width,height,yuv);
    }
    ByteBuffer buffer=getInputBuffer(mVideoEnc,index);
    buffer.clear();
    buffer.put(yuv);
    //结束时,发送结束标志,在编码完成后结束
    mVideoEnc.queueInputBuffer(index,0,yuv.length,
        mStartFlag?0:MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}
MediaCodec.BufferInfo mInfo=new MediaCodec.BufferInfo();
int outIndex=mVideoEnc.dequeueOutputBuffer(mInfo,0);
do {
    if(outIndex>=0){
        ByteBuffer outBuf=getOutputBuffer(mVideoEnc,outIndex);
        //里面不在是写入到文件,而是写入为混合器的sample data
        if(mTrackCount==3&&mInfo.size>0){
            mMuxer.writeSampleData(mVideoTrack,outBuf,mInfo);
        }
        mVideoEnc.releaseOutputBuffer(outIndex,false);
        outIndex=mVideoEnc.dequeueOutputBuffer(mInfo,0);
        Log.e("wuwang","outIndex-->"+outIndex);
        //编码结束的标志
        if((mInfo.flags&MediaCodec.BUFFER_FLAG_END_OF_STREAM)!=0){
            return true;
        }
    }else if(outIndex==MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
       //按照MediaMuxer中所说,加入轨道的时机在这里
        mVideoTrack=mMuxer.addTrack(mVideoEnc.getOutputFormat());
        Log.e("wuwang","video track-->"+mVideoTrack);
        mTrackCount++;
        //一定要音轨视频轨都加入后,再开始混合
        if(mTrackCount==2){
            mMuxer.start();
            mTrackCount=3;
        }
    }
}while (outIndex>=0);

当然是用MediaMuxer前,肯定是需要创建一个MediaMuxer的实例的:

mMuxer=new MediaMuxer(path+"."+postfix, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

短视频系统音频的操作和视频一样更改,将音频编码也加入MeidaMuxer的轨道中,得到一个轨道索引,将编码后的数据加入为MediaMuxer当前音轨的sample data。音轨和上面的视轨各自做各自的,结束录制时,都发送结束标志,然后在短视频系统编码结束后,停止混合器就可以得到一个固定码率的MP4文件了。

总结

至此,有关Android短视频系统硬编码博客就结束了。但是在实际使用MediaCodec和MediaMuxer的过程中,总会遇到这样或者那样的问题,硬编硬解,和硬件相关比较紧密,Android虽然提供了一个很好的API,但是各个厂商在实现的过程中,总是会做些让自己变得独特的事情。当然他们的目的并不是为了独特,有的是为了让产品变得更优秀(虽然最后可能会做砸了),有的是为了省钱,用软件去弥补硬件的缺陷,最后的结果就是苦了做上层开发的码农们。
从博主在使用MediaCodec和MediaMuxer的过程中遇到的问题,总结下需要注意主要有以下几点:

MediaCodec是Android4.1新增API,MediaMuxer是Android4.3新增API。
颜色空间。按照Android本身的意思,COLOR_FormatYUV420Planar应该是所有硬件平台都支持的。但是实际上并不是这样。所以在设置颜色空间时,应该获取硬件平台所支持的颜色空间,确保它是支持你打算使用的颜色空间,不支持的话应该启用备用方案(使用其他当前硬件支持的颜色空间)。
视频尺寸,在一些手机上,短视频系统视频录制的尺寸可以是任意的。但是有些手机,不支持的尺寸设置会导致录制的视频现错乱。博主在使用Oppo R7测试,360*640的视频,单独录制视频没问题,音视频混合后,出现了颜色错乱的情况,而在360F4手机上,却都是没问题的。将视频宽高都设置为16的倍数,可以解决这个问题。
短视频系统编码器格式设置,诸如音频编码的采样率、比特率等,取值也需要结合硬件平台来设置,否则也会导致崩溃或其他问题。这个其实和颜色空间的选择一样。
网上看到许多queueInputBuffer中设置presentationTimeUs为System.nanoTime()/1000,这样做会导致编码出来的音视频,在播放时,总时长显示的是错误的。应该记录开始时候的nanoTime,然后设置presentationTimeUs为(System.nanoTime()-nanoTime)/1000。
短视频系统录制结束时,应该发送结束标志MediaCodec.BUFFER_FLAG_END_OF_STREAM,在编码后区获得这个标志时再终止循环,而不是直接终止循环。
应该还有其他需要注意的问题。我暂时还没遇到。
————————————————

声明:声明:本文由云豹科技转发自CSDN【湖广午王】博客,如有侵权请联系作者删除
原文链接:https://blog.csdn.net/junzia/article/details/54018671

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值