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

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. 云原生周刊:HashiCorp Vault 1.14 发布 | 2023.6.26

    开源项目推荐 Helmfile Helmfile 是一个开源工具,使用 Helm charts 简化复杂应用程序的部署.它提供了一种声明性的方式来定义 Kubernetes 资源的期望状态,并管理 H ...

  2. python项目实战——一元线性回归预测模型

    文章目录 1.一元线性回归简介 2.环境准备 3.数据准备 4.可视化数据 5.构建线性回归模型 在数据科学领域,预测分析是一项核心技能.一元线性回归作为预测分析的基石,能够帮助我们理解一个自变量如何 ...

  3. Flink Checkpoint & Savepoint

    Flink checkpoint Checkpoint是Flink实现容错机制最核心的功能,能够根据配置周期性地基于Stream中各个Operator的状态来生成Snapshot,从而将这些状态数据定 ...

  4. quartz集群增强版🎉

    quartz集群增强版 转载请著名出处https://www.cnblogs.com/funnyzpc/p/18534034 这是除了mee_admin之外,投入时间精力最多的一次开源了,quartz ...

  5. 无加密的机密性:Chaffing and Winnowing原理和C#实验仿真

    最近在Crypto 2023上看到一篇有趣的文章[1],其旨在一个存在拥有所有密钥并知道所有消息的"独裁者"的信道中,通过安排与常规密文无法区分的隐藏的"变形" ...

  6. Spring + EHcache配置

    需要使用Spring来实现一个Cache简单的解决方案,具体需求如下:使用任意一个现有开源Cache Framework,要求可以Cache系统中Service或则DAO层的get/find等方法返回 ...

  7. 编译器-FIRST集合(补充:左递归)

    上一篇中实现的First函数没有考虑左递归,在这对此说明和实现 1.立即左递归 A -> Ab|a 1.两步或两步以上产生的左递归 A -> Bc|a B -> Ab|d 前面的实现 ...

  8. three.js优化

    Three js 开发的一些知识整理,方便后期遇到类似的问题,能够及时查阅使用. three.js 性能优化方面,整理一下常用的优化方法或者方向,供大家一个优化思考的方向 尽量重用Material和G ...

  9. 使用Flex布局的几个小技巧

    前情 Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性,任何一个容器都可以指定为 Flex 布局,如果说目前我开发中离不开的布局方式 ...

  10. uni-app小程序(抖音)text组件使用踩坑

    前情 uni-app是我比较喜欢的跨平台框架,它能开发小程序/H5/APP(安卓/iOS),重要的是对前端开发友好,自带的IDE让开发体验也挺棒的,公司项目就是主推uni-app. 坑位 最近在开发一 ...