刷抖音上瘾后,决定探究如果做一款类似抖音短视频app

二、短视频处理播放

视频的处理和播放主要是视频的清晰度、观看流畅度方面的体验。在这方面来讲,可以采用“窄带高清”技术,在节省码率的同时能够提供更加清晰的观看体验,经过测试,同等视频质量下最高可以节省20-40%带宽。除了带宽之外,短视频内容的存储和CDN优化也尤为重要,通常我们需要上传到云存储服务器的内容是短视频内容和封面内容。

而CDN优化带给短视频平台的则是进一步的短视频首次载入和循环播放方面的体验。比如针对首播慢的问题,像阿里云播放器支持QUIC协议,基于CDN的调度,可以使短视频首次播放秒开的成功率达到98%,此外在循环播放时还可以边播放边缓存,用户反复观看某一短视频时就不用耗费流量了。

三、录制视频的方式

在Android系统当中,如果需要一台Android设备来获取到一个MP4这样的视频文件的话,主流的方式一共与三种:MediaRecorder、MediaCodec+MediaMuxer、FFmpeg。

MediaRecorder:是Android系统直接提供给我们的录制类,用于录制音频和视频的一个类,简单方便,不需要理会中间录制过程,结束录制后可以直接得到音频文件进行播放,录制的音频文件是经过压缩的,需要设置编码器,录制的音频文件可以用系统自带的播放器播放。

  • 优点:大部分以及集成,直接调用相关接口即可,代码量小,简单稳定;

  • 缺点:无法实时处理音频;输出的音频格式不是很多。

MediaCodec+MediaMuxer: MediaCodec 与 MediaMuxer结合使用同样能够实现录制的功能。MediaCodec是Android提供的编解码类,MediaMuxer则是复用类(生成视频文件)。从易用性的角度上来说肯定不如MediaRecorder,但是允许我们进行更加灵活的操作,比如需要给录制的视频添加水印等各种效果。

  • 优点: 与MediaRecorder一样低功耗速度快,并且更加灵活

  • 缺点: 支持的格式有限,兼容性问题

FFmpeg: FFmpeg(Fast forword mpeg,音视频转换器)是一个开源免费跨平台的视频和音频流方案,它提供了录制/音视频编解码、转换以及流化音视频的完整解决方案。主要的作用在于对多媒体数据进行解协议、解封装、解码以及转码等操作

  • 优点:格式支持非常的强,十分的灵活,功能强大,兼容性好;

  • 缺点:C语言些的音视频编解码程序,使用起来不是很方便。

虽然从数据看来FFmpeg是最好的,但是我们得首先排除这种,因为他的易用性是最差的;其次,MediaRecorder也是需要排除的,所以在这里我比较推荐MediaCodec+MediaMuxer这种方式。

四、编码器参数

码率:数据传输时单位时间传送的数据位数,kbps:千位每秒。码率和质量成正比,也和文件体积成正比。码率超过一定数值,对图像的质量没有多大的影响。

帧数:每秒显示多少个画面,fps

关键帧间隔:在H.264编码中,编码后输出的压缩图像数据有多种,可以简单的分为关键帧和非关键帧。关键帧能够进行独立解码,看成是一个图像经过压缩的产物。而非关键帧包含了与其他帧的“差异”信息,也可以称呼为“参考帧”,它的解码需要参考关键帧才能够解码出一个图像。非关键帧拥有更高的压缩率。

五、MediaCodec+MediaMuxer的使用

MediaMuxer和MediaCodec这两个类,它们的参考文http://developer.android.com/reference/android/media/MediaMuxer.html和http://developer.android.com/reference/android/media/MediaCodec.html,里边有使用的框架。这个组合可以实现很多功能,比如音视频文件的编辑(结合MediaExtractor),用OpenGL绘制Surface并生成mp4文件,屏幕录像以及类似Camera app里的录像功能(虽然这个用MediaRecorder更合适)等。

它们一个是生成视频,一个生成音频,这里把它们结合一下,同时生成音频和视频。基本框架和流程如下:

首先是录音线程,主要参考HWEncoderExperiments。通过AudioRecord类接收来自麦克风的采样数据,然后丢给Encoder准备编码:

AudioRecord audio_recorder; 
audio_recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 
SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, buffer_size); 
// … 
audio_recorder.startRecording(); 
while (is_recording) { 
byte[] this_buffer = new byte[frame_buffer_size]; 
read_result = audio_recorder.read(this_buffer, 0, frame_buffer_size); // read audio raw data 
// … 
presentationTimeStamp = System.nanoTime() / 1000; 
audioEncoder.offerAudioEncoder(this_buffer.clone(), presentationTimeStamp); // feed to audio encoder

}

这里也可以设置AudioRecord的回调(通过setRecordPositionUpdateListener())来触发音频数据的读取。offerAudioEncoder()里主要是把audio采样数据送入音频MediaCodec的InputBuffer进行编码:

ByteBuffer[] inputBuffers = mAudioEncoder.getInputBuffers(); 
int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(-1); 
if (inputBufferIndex >= 0) { 
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; 
inputBuffer.clear(); 
inputBuffer.put(this_buffer); 
… 
mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, this_buffer.length, presentationTimeStamp, 0); 
}

下面,参考Grafika-SoftInputSurfaceActivity,并加入音频处理。主循环大体分四部分:

try { 
// Part 1 
prepareEncoder(outputFile); 
… 
// Part 2 
for (int i = 0; i < NUM_FRAMES; i++) { 
generateFrame(i); 
drainVideoEncoder(false); 
drainAudioEncoder(false); 

// Part 3 
… 
drainVideoEncoder(true); 
drainAudioEncoder(true); 
} catch (IOException ioe) { 
throw new RuntimeException(ioe); 
} finally { 
// Part 4 
releaseEncoder(); 
}

第1部分是准备工作,除了video的MediaCodec,这里还初始化了audio的MediaCodec:

MediaFormat audioFormat = new MediaFormat(); 
audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100); 
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); 
… 
mAudioEncoder = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE); 
mAudioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
mAudioEncoder.start();

第2部分进入主循环,app在Surface上直接绘图,由于这个Surface是从MediaCodec中用createInputSurface()申请来的,所以画完后不用显式用queueInputBuffer()交给Encoder。drainVideoEncoder()和drainAudioEncoder()分别将编码好的音视频从buffer中拿出来(通过dequeueOutputBuffer()),然后交由MediaMuxer进行混合(通过writeSampleData())。注意音视频通过PTS(Presentation time stamp,决定了某一帧的音视频数据何时显示或播放)来同步,音频的time stamp需在AudioRecord从MIC采集到数据时获取并放到相应的bufferInfo中,视频由于是在Surface上画,因此直接用dequeueOutputBuffer()出来的bufferInfo中的就行,最后将编码好的数据送去MediaMuxer进行多路混合。

注意这里Muxer要等把audio track和video track都加入了再开始。MediaCodec在一开始调用dequeueOutputBuffer()时会返回一次INFO_OUTPUT_FORMAT_CHANGED消息。我们只需在这里获取该MediaCodec的format,并注册到MediaMuxer里。接着判断当前audio track和video track是否都已就绪,如果是的话就启动Muxer。

总结来说,drainVideoEncoder()的主逻辑大致如下,drainAudioEncoder也是类似的,只是把video的MediaCodec换成audio的MediaCodec即可。

while(true) { 
int encoderStatus = mVideoEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC); 
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { 
… 
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 
encoderOutputBuffers = mVideoEncoder.getOutputBuffers(); 
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 
MediaFormat newFormat = mAudioEncoder.getOutputFormat(); 
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

这里我特地整理了一份《Android开发核心知识点笔记》,里面就包含了自定义View相关的内容

除了这份笔记,还给大家分享 Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。

分享上面这些资源,希望可以帮助到大家提升进阶,如果你觉得还算有用的话,不妨把它们推荐给你的朋友~

喜欢本文的话,给我点个小赞、评论区留言或者转发支持一下呗~

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

的朋友~**

喜欢本文的话,给我点个小赞、评论区留言或者转发支持一下呗~

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值