本文由陆业聪分享,原题“一文掌握直播技术:实时音视频采集、编码、传输与播放”,本文进行了排版和内容优化。

1、引言

从游戏、教育、电商到娱乐,直播技术的应用场景无处不在。随着移动端的网速越来越快,直播技术的普及和发展将更加迅速。

本文详细介绍了Android端直播技术的全貌,涵盖了从实时音视频采集、编码、传输到解码与播放的各个环节。文章还探讨了直播中音视频同步、编解码器选择、传输协议以及直播延迟优化等关键问题。希望本文能为你提供有关Andriod端直播技术的深入理解和实践指导。

 

2、系列文章

本文是系列文章中的第 11 篇,本系列总目录如下:

3、知识准备

音视频技术的门槛一直以来都相对较高,如果你对音视频相关技术的理论知识了解不多,建议务必优先阅读这几篇零基础音视频入门文章:

  1. 零基础,史上最通俗视频编码技术入门
  2. 爱奇艺技术分享:轻松诙谐,讲解视频编解码技术的过去、现在和将来
  3. 零基础入门:实时音视频技术基础知识全面盘点
  4. 快速掌握11个视频技术相关的基础概念

另外两篇入门提纳式的文章也可以一并阅读:

  1. 写给小白的实时音视频技术入门提纲
  2. 福利贴:最全实时音视频开发要用到的开源工程汇总

以上资料学习完成后,再回头来阅读本篇效果会更好一点。

4、实时音视频采集

4.1音视频采集设备与API

在 Android 设备中,音视频的采集主要依赖于摄像头和麦克风这两个硬件设备。摄像头负责图像的采集,麦克风则负责音频的采集。

为了调用这两个设备,Android 提供了 Camera API 和 AudioRecord API。通过这两个 API,我们可以方便地控制设备,获取音视频数据。

以下是具体实践步骤。

1)使用 Camera 或 Camera2 API 来调用摄像头:

// Camera API

Camera camera = Camera.open();

Camera.Parameters parameters = camera.getParameters();

parameters.setPreviewSize(width, height);

camera.setParameters(parameters);

camera.setPreviewCallback(previewCallback);

camera.startPreview();

// Camera2 API

CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

String cameraId = cameraManager.getCameraIdList()[0];

CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);

StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

Size[] previewSizes = map.getOutputSizes(SurfaceTexture.class);

// 选择合适的预览尺寸

cameraManager.openCamera(cameraId, stateCallback, null);

2)使用 AudioRecord API 来调用麦克风:

int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);

AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, bufferSize);

audioRecord.startRecording();

4.2音视频采集参数设置

音视频采集的质量和流畅度,很大程度上取决于采集参数的设置。这些参数包括分辨率、帧率和码率等。

具体是:

  • 1)分辨率:决定了图像的清晰度。高分辨率可以得到更清晰的图像,但也会增加数据量,可能导致网络传输压力增大;
  • 2)帧率:决定了视频的流畅度。高帧率可以得到更流畅的视频,但同样会增加数据量;
  • 3)码率:决定了音视频数据的压缩程度。高码率可以得到更高质量的音视频,但也会增加数据量。

在设置音视频采集参数时,需要根据网络状况和设备性能,做出合适的折衷。

以下是具体实践步骤。

1)设置摄像头的分辨率和帧率:

Camera.Parameters parameters = camera.getParameters();

parameters.setPreviewSize(width, height);

parameters.setPreviewFrameRate(frameRate);

camera.setParameters(parameters);

2)设置 AudioRecord 的采样率、声道数和音频格式:

int sampleRate = 44100;

int channelConfig = AudioFormat.CHANNEL_IN_MONO;

int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

4.3音视频同步与时间戳处理

在直播中,音视频同步是一个重要的问题。

为了实现同步,我们需要为每帧音视频数据添加时间戳。时间戳记录了数据的采集时间,可以用来调整播放顺序,保证音视频的同步。在解码和播放时,播放器会根据时间戳,正确地排列和播放音视频数据。

为了处理视频帧数据和时间戳,我们需要将采集到的音视频帧数据和对应的时间戳封装成一个数据结构,然后将这个结构传递给编码器和传输模块。

以下是一个简单的处理方法。

1)首先,定义一个数据结构来保存音视频帧数据和时间戳:

public class FrameData {

public byte[] data;

public long timestamp;

public FrameData(byte[] data, long timestamp) {

this.data = data;

this.timestamp = timestamp;

}

}

2)在摄像头的预览回调中添加时间戳:

camera.setPreviewCallback(new Camera.PreviewCallback() {

@Override

public void onPreviewFrame(byte[] data, Camera camera) {

long timestamp = System.nanoTime();

// 处理视频帧数据和时间戳

FrameData frameData = new FrameData(data, timestamp);

// 将 frameData 传递给编码器和传输模块

}

});

3)在 AudioRecord 的录音循环中添加时间戳:

while (isRecording) {

long timestamp = System.nanoTime();

int bytesRead = audioRecord.read(buffer, 0, bufferSize);

// 处理音频帧数据和时间戳

FrameData frameData = new FrameData(Arrays.copyOf(buffer, bytesRead), timestamp);

// 将 frameData 传递给编码器和传输模块

}

4)在编码器和传输模块中,根据FrameData对象的时间戳来处理音视频帧数据。

例如,在编码时,将时间戳作为编码后的音视频数据的显示时间;在传输时,根据时间戳来调整发送顺序和发送速度。

这样,在解码和播放时,播放器可以根据时间戳正确地排列和播放音视频数据,实现同步。

5、音频编码

5.1音频编码格式对比

常见的音频编码格式有 AAC 和 Opus 等。AAC 具有较高的编码效率,而 Opus 则在实时通信中表现更优。

具体是:

  • 1)AAC编码格式:适用于非实时通信领域,如音乐、广播、视频等,具有较高的编码效率和广泛的设备兼容性,但在实时通信中的延迟优化较弱;
  • 2)Opus编码格式:适用于实时通信领域,如VoIP、在线会议、游戏语音等,具有高音质、低延迟和强网络适应性,但设备兼容性相对不如AAC。

5.2在Android中实现音频编码

在 Android 中实现音频编码,可以使用 Android 提供的 MediaCodec 类。MediaCodec 支持多种音频编码格式,如 AAC 和 Opus 等。要选择合适的编码格式,可以参考以下步骤。

1)创建一个 MediaCodec 编码器实例:

MediaCodec audioEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);

2)配置编码器参数:

MediaFormat audioFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRate, channelCount);

audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);

audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);

audioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

3)开始编码:

audioEncoder.start();

6、视频编码

6.1视频编码格式对比

常见的视频编码格式有 H.264、H.265 和 VP8 等。H.264 是当前最常用的编码格式,而 H.265 和 VP8 则在特定场景下有更好的性能。

具体是:

  • 1)H.264编码格式:适用于视频会议、网络直播、视频分享等场景,具有较高的压缩效率和广泛的设备兼容性,但压缩效率相比H.265较低;
  • 2)H.265编码格式:适用于4K、8K超高清视频、虚拟现实等需要高分辨率和高画质的场景,具有极高的压缩效率,但编解码复杂度高,需要更强的计算能力,且设备兼容性相对不如H.264;
  • 3)VP8编码格式:适用于网络视频通话、在线视频服务等对开源和免费有要求的场景,延迟低,适合实时通信,但压缩效率和视频质量不如H.264和H.265,且设备兼容性较差。

6.2 在Android中实现视频编码

在 Android 中实现视频编码,同样可以使用 MediaCodec 类。要选择合适的编码格式,可以参考以下步骤。

1)创建一个 MediaCodec 编码器实例:

MediaCodec videoEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);

2)配置编码器参数:

MediaFormat videoFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);

videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);

videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);

videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);

videoEncoder.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

3)开始编码:

videoEncoder.start();

7、硬件编码与软件编码的选择与优缺点

硬件编码利用 GPU 进行编码,性能更高,但兼容性较差;软件编码则兼容性更好,但性能较低。在实际应用中,需要根据设备性能和需求进行选择。

在 Android 中,MediaCodec 类会根据设备性能和需求自动选择硬件编码器或软件编码器。要强制使用硬件编码器或软件编码器,可以在创建 MediaCodec 实例时,指定编码器名称。

例如,要使用硬件 H.264 编码器,可以使用以下代码:

MediaCodec videoEncoder = MediaCodec.createByCodecName("OMX.google.h264.encoder");

8、传输协议

9、音视频解码与播放

9.1音视频解码器的选择与性能优化

解码器的选择会影响播放质量和性能。通常,硬件解码器性能更高,但兼容性较差;软件解码器兼容性较好,但性能较低。在实际应用中,需要根据设备性能和需求进行选择。

在 Android 中,解码器的选择可以通过 MediaCodec 类来实现。MediaCodec 支持硬件解码和软件解码,通常情况下,它会根据设备性能和需求自动选择解码器。

9.2音视频渲染与同步策略

在渲染音视频时,需要保证音视频同步。可以通过校准时间戳或者调整播放速度等方法实现同步。

在 Android 中,音视频的渲染可以通过 SurfaceView 或 TextureView 来实现。为了保证音视频同步,可以在渲染每帧数据时,根据时间戳来调整渲染速度。

以下是具体实践步骤。

1)创建一个 SurfaceView 或 TextureView:

SurfaceView surfaceView = new SurfaceView(context);

// 或

TextureView textureView = new TextureView(context);

2)在解码每帧数据时,根据时间戳来调整渲染速度:

long presentationTimeUs = bufferInfo.presentationTimeUs;

long delayUs = presentationTimeUs - System.nanoTime() / 1000;

if (delayUs > 0) {

Thread.sleep(delayUs / 1000);

}

decoder.releaseOutputBuffer(outputBufferIndex, true);

9.3播放器的缓冲与自适应码率调整

为了应对网络波动,播放器需要设置合适的缓冲策略。自适应码率调整则可以根据网络状况动态调整视频质量,以保证流畅度。

在 Android 中,播放器的缓冲策略可以通过 MediaPlayer 或 ExoPlayer 的 API 来设置。自适应码率调整则可以通过 ExoPlayer 的 TrackSelection API 来实现。

以下是具体实践步骤。

1)设置播放器的缓冲策略:

MediaPlayer mediaPlayer = new MediaPlayer();

mediaPlayer.setBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {

@Override

public void onBufferingUpdate(MediaPlayer mp, int percent) {

// 更新缓冲进度

}

});

// 或

ExoPlayer exoPlayer = new SimpleExoPlayer.Builder(context).build();

exoPlayer.setBufferedPositionUpdateListener(new ExoPlayer.BufferedPositionUpdateListener() {

@Override

public void onBufferedPositionUpdate(long bufferedPosition) {

// 更新缓冲进度

}

});

2)设置自适应码率调整:

TrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory();

DefaultTrackSelector trackSelector = new DefaultTrackSelector(context, trackSelectionFactory);

ExoPlayer exoPlayer = new SimpleExoPlayer.Builder(context).setTrackSelector(trackSelector).build();

10、直播架构概述

10.1直播架构图

以下是直播架构图:

解释一下:

  • 1)推流端:需要实现音视频采集、编码、传输等功能。关键组件包括采集模块、编码器、传输模块等;
  • 2)服务器端:负责接收、转发和存储音视频数据。关键组件包括负载均衡、转码、录制等功能模块;
  • 3)拉流端:需要实现音视频解码、渲染和播放等功能。关键组件包括解码器、渲染模块、播放器等。

10.2直播延迟与优化策略

直播延迟会影响用户体验。通过优化采集、编码、传输、解码等环节,可以降低延迟,提高实时性。

直播延迟优化策略有:

  • 1)优化采集模块:提高采集效率,减少数据处理时间;
  • 2)优化编码器:选择性能更高的编码器,减少编码时间;
  • 3)优化传输模块:优化网络传输策略,如使用更快的传输协议、提高网络带宽等;
  • 4)优化解码器:选择性能更高的解码器,减少解码时间;
  • 5)优化渲染模块和播放器:提高渲染效率,减少播放延迟。

11、本文小结

本文介绍了直播技术的全貌,涉及实时音视频采集到播放的各个环节。

以下是一个简化的直播流程图:

直播流程包括以下几个关键环节:

  • 1)实时音视频采集:通过摄像头和麦克风采集音视频数据,并进行参数设置和同步处理;
  • 2)音视频编码:将采集到的音视频数据进行编码,以便进行传输。选择合适的编码器和编码格式,如AAC、Opus、H.264、H.265和VP8等;
  • 3)传输协议:选择合适的传输协议,如RTMP、HLS和WebRTC等,以保证音视频数据的实时传输;
  • 4)服务器处理:服务器接收、转发和存储音视频数据,进行负载均衡、转码和录制等处理;
  • 5)音视频解码与播放:将接收到的音视频数据进行解码、渲染和播放,实现音视频同步和延迟优化。

在实际应用中,需要根据需求和场景选择合适的技术和策略,以实现高质量、低延迟的直播体验。

12、参考资料

[1] 详解音频编解码的原理、演进和应用选型

[2] 零基础,史上最通俗视频编码技术入门

[3] 理解实时音视频聊天中的延时问题一篇就够

[4] 浅谈开发实时视频直播平台的技术要点

[5] 福利贴:最全实时音视频开发要用到的开源工程汇总

[6] 爱奇艺技术分享:轻松诙谐,讲解视频编解码技术的过去、现在和将来

[7] 零基础入门:实时音视频技术基础知识全面盘点

[8] 实时音视频面视必备:快速掌握11个视频技术相关的基础概念

[9] 理论联系实际:实现一个简单地基于html]5的实时视频直播

[10] 实时视频直播客户端技术盘点:Native、html]5、WebRTC、微信小程序

[11] Android直播入门实践:动手搭建一套简单的直播系统

[12] 视频直播技术干货:一文读懂主流视频直播系统的推拉流架构、传输协议等

[13] 零基础入门:基于开源WebRTC,从0到1实现实时音视频聊天功能

[14] 实时音视频入门学习:开源工程WebRTC的技术原理和使用浅析

[15] 实时音视频开发理论必备:如何省流量?视频高度压缩背后的预测技术

[16] 万字长文详解QQ Linux端实时音视频背后的跨平台实践

(本文已同步发布于:http://www.52im.net/thread-4714-1-1.html

视频直播技术干货(十二):从入门到放弃,快速学习Android端直播技术的更多相关文章

  1. Spring+SpringMVC+MyBatis深入学习及搭建(十二)——SpringMVC入门程序(一)

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6999743.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十一)——S ...

  2. 《Django By Example》第十二章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第十二章,全书最后一章,终于到这章 ...

  3. 《vue.js2.0从入门到放弃》学习之路

    原文地址: Vue.js2.0从入门到放弃---入门实例(一):http://blog.csdn.net/u013182762/article/details/53021374 Vue.js2.0从入 ...

  4. 【第十二篇】- Git 服务器搭建之Spring Cloud直播商城 b2b2c电子商务技术总结

    Git 服务器搭建 上一章节中我们远程仓库使用了 Github,Github 公开的项目是免费的,2019 年开始 Github 私有存储库也可以无限制使用. 这当然我们也可以自己搭建一台 Git 服 ...

  5. Android IOS WebRTC 音视频开发总结(十二)-- sufaceview

    谈到音视频不得不谈谈对视频呈现的理解,为了让大家能有一个更好的理解,先看看android里面SurfaceView的原理,后续陆续分享其绘画原理. 说明:本文是转载的,转载自哪里我也不知道,貌似经过很 ...

  6. Android IOS WebRTC 音视频开发总结(十)-- webrtc入门002

    继续上一篇中未翻译完成的部分,主要包括下面三个部分: 1,扩展:WebRTC多方通话. 2,MCU Multipoint Control Unit. 2, 扩展:VOIP,电话,消息通讯. 注意:翻译 ...

  7. opencv探索之路(十二):感兴趣区域ROI和logo添加技术

    在图像处理领域,有一个非常重要的名词ROI. 什么是ROI? 它的英文全称是Region Of Interest,对应的中文解释就是感兴趣区域. 感兴趣区域,就是我们从图像中选择一个图像区域,这个区域 ...

  8. Spring+SpringMVC+MyBatis深入学习及搭建(十二)——SpringMVC入门程序

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6999743.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十一)--S ...

  9. 第四十二篇 入门机器学习——Numpy的基本操作——索引相关

    No.1. 使用np.argmin和np.argmax来获取向量元素中最小值和最大值的索引 No.2. 使用np.random.shuffle将向量中的元素顺序打乱,操作后,原向量发生改变:使用np. ...

  10. Jmeter(十二) - 从入门到精通 - JMeter逻辑控制器 - 终篇(详解教程)

    1.简介 Jmeter官网对逻辑控制器的解释是:“Logic Controllers determine the order in which Samplers are processed.”. 意思 ...

随机推荐

  1. OpenFunction 1.2.0 发布:集成 KEDA http-addon 作为同步函数运行时

    OpenFunction 是一个开源的云原生 FaaS(Function as a Service,函数即服务)平台,旨在帮助开发者专注于业务逻辑的研发.我们非常高兴地宣布 OpenFunction ...

  2. 华为Ensp拓扑,使用MSTP、OSPF、DHCP、VRRP、链路聚合、CHAP

    OSPF+DHCP+VRRP+Eth-trunk+PPP(CHAP)+MSTP 实验目标: LSW1和LSW2核心交换机互为备份,配置链路聚合,设备冗余设计,LSW1和LSW2作为核心交换机配置DHC ...

  3. 强化学习算法笔记之【DDPG算法】

    强化学习笔记之[DDPG算法] 目录 强化学习笔记之[DDPG算法] 前言: 原论文伪代码 DDPG 中的四个网络 代码核心更新公式 前言: 本文为强化学习笔记第二篇,第一篇讲的是Q-learning ...

  4. WebUploader 文件上传,兼容ios和安卓

    var upImg = WebUploader.create({ auto: true, swf: 'webuploader-0.1.5/Uploader.swf', // 图片接收服务端. serv ...

  5. 4年经验来面试20K的测试岗,连基础都不会,还不如招应届生。

    公司前段时间缺人,也面了不少测试,结果竟然没有一个合适的.一开始瞄准的就是中级的水准,也没指望来大牛,提供的薪资在10-20k,面试的人很多,但平均水平很让人失望.看简历很多都是3.4年工作经验,但面 ...

  6. [这可能是最好的Spring教程!]Maven的模块管理——如何拆分大项目并且用parent继承保证代码的简介性

    问题的提出 在软件开发中,我们为了减少软件的复杂度,是不会把所有的功能都塞进一个模块之中的,塞在一个模块之中对于软件的管理无疑是极其困难且复杂的.所以把一个项目拆分为模块无疑是一个好方法 ┌ ─ ─ ...

  7. SQL Server如何定期自动备份数据库

    打开SQL Server代理服务 实现自动备份功能,首先要保证SQL Server的"SQL Server(代理)"服务已经打开. 如果没有看到这个"SQL Server ...

  8. 解决Delphi应用程序向桌面资源管理拖放问题 win10,winxp,win2003测试成功

    解决Delphi应用程序向桌面资源管理拖放问题 win10,winxp,win2003测试成功 如果是实体拖动,是比较好解决的. 如果是虚拟应用程序数据,拖动到本机桌面或资源管理器目录下,获取目录路径 ...

  9. CF2030E MEXimize the Score 题解

    题面 假设我们将数组 \(b\) 中的元素分割成任意多个 \(k\) 的非空多集 \(S_1, S_2, \ldots, S_k\) ,其中 \(k\) 是一个任意的正整数.定义 \(b\) 的分值为 ...

  10. TSCTF-J2024 密码向WP(5/8)

    ezRSA part 1 #part1 p = getPrime(512) q = getPrime(512) n = p * q phi = (p-1) * (q-1) d = getPrime(2 ...