Jack Jiang

我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
posts - 467, comments - 13, trackbacks - 0, articles - 0

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

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



作者:Jack Jiang (点击作者姓名进入Github)
出处:http://www.52im.net/space-uid-1.html
交流:欢迎加入即时通讯开发交流群 215891622
讨论:http://www.52im.net/
Jack Jiang同时是【原创Java Swing外观工程BeautyEye】【轻量级移动端即时通讯框架MobileIMSDK】的作者,可前往下载交流。
本博文 欢迎转载,转载请注明出处(也可前往 我的52im.net 找到我)。


只有注册用户登录后才能发表评论。


网站导航:
 
Jack Jiang的 Mail: jb2011@163.com, 联系QQ: 413980957, 微信: hellojackjiang