从零开发一款AndroidRTMP播放器

阅读: 评论:0

从零开发⼀款AndroidRTMP播放
1. 背景介绍
15年移动端直播应⽤⽕起来的时候,主要的直播协议是RTMP,多媒体服务以Adobe的AMS、wowza、Red5、crtmpserver、nginx rtmp module等,后⾯过长RTMP服务SRS开始流⾏。Android端播放器主要以开始以EXOPlayer播放HLS,但是HLS有延迟⾼的确定,随后⼤家主要使⽤开源的ijkplyer,ijkplayer通过ffmpeg进⾏拉流及解码,⽀持多种⾳视频编码,还有跨平台,API与系统播放器保持⼀致等特征,后续各⼤⼚提供的直播SDK均有ijkplayer⾝影。
当时在做⼀款游戏SDK,SDK主要提供了游戏画⾯声⾳采集、⾳视频编解码、直播推流、直播拉流播放等,SDK为游戏提供直播功能,播放也是采⽤了现成的ijkplayer播放器。但是SDK推⼴的时候遇到了问题,游戏⼚家嫌弃SDK体积⼤(其实总共也就3Mb左右),我们需要⼀款体积⼩,性能⾼的播放器,由于开发成本的原因⼀直没有时间做,后⾯换⼯作期间,花了⼀个⽉时间把这款播放器开发出来,并开源了出来。oarplayer 是基于MediaCodec与srs-librtmp,完全不依赖ffmpeg,纯C语⾔实现的播放器。本⽂主要介绍这款播放器的实现思路。
2. 整体架构设计
播放器整体播放流程如下:
通过srs-librtmp拉取直播流,通过package type分离⾳视频流,将package数据缓存到package队列,解码线程不断从package队列读取package交由解码器解码,解码器将解码后的frame存储到frame队列,opensles播放线程与opengles渲染线程从frame队列读取frame播放与渲染,这⾥还涉及到⾳视频同步。
播放器主要涉及了以下线程:
石墨保护套1. rtmp拉流线程;
2. ⾳频解码线程;
3. 视频解码线程;
4. ⾳频播放线程;阻尼电机
氚电池
5. 视频渲染线程;
6. JNI回调线程。
fm03. API接⼝设计
通过以下⼏步即可完成rtmp播放:
1. 实例化OARPlayer:OARPlayer player = new OARPlayer();
2. 设置视频源:player.setDataSource(rtmp_url);
3. 设置surface:player.Holder());
4. 开始播放:player.start();
5. 停⽌播放:player.stop();
6. 释放资源:lease();
Java层⽅法封装了JNI层⽅法,JNI层封装调⽤了对应的具体功能。
4. rtmp拉流线程
oarplayer使⽤的是srs-librtmp,srs-librtmp是从SRS服务器导出的⼀个客户端库,作者提供srs-librtmp初衷是:
1. 觉得rtmpdump/librtmp的代码太难读了,⽽SRS的代码可读性很好;
2. 压测⼯具srs-bench是个客户端,需要⼀个客户端库;
3. 觉得服务器能搞好,客户端也不在话下
⽬前srs-librtmp作者已经停⽌维护,主要原因如作者所说:
决定开源项⽬正义的绝对不是技术多好,⽽是能跑多久。技术很⽜,性能很强,代码风格很好,固然是个好事,但是这些都顶不上⼀个“不维护”的⼤罪过,代码放出来不维护,怎么跟进业内技术的不断
发展呢。⽽决定能跑多久的,⾸先是技术热情,然后是维护者的领域背景。SRS的维护者都是服务器背景,⼤家的⼯作都是在服务器,客户端经验太少了,⽆法长久维护客户端的库。因此,SRS决定果断放弃srs-librtmp,不再维护客户端库,聚焦于服务器的快速迭代。客户端并⾮不重要,⽽是要交给专业的客户端的开源项⽬和朋友维护,⽐如FFmpeg也⾃⼰实现了librtmp。
oarplayer当初使⽤srs-librtmp是基于srs-librtmp代码的可读性考虑。oarplayer有相当⾼的模块化特性,可以很⽅便的替换各个rtmp lib实现。这⾥介绍srs-librtmp接⼝:
1. 创建srs_rtmp_t对象:srs_rtmp_create(url);
2. 设置读写超时时间:srs_rtmp_set_timeout;
3. 开始握⼿:srs_rtmp_handshake;
4. 开始连接:srs_rtmp_connect_app;
5. 设置播放模式:srs_rtmp_play_stream;
6. 循环读取⾳视频包:srs_rtmp_read_packet(rtmp, &type, ×tamp, &data, &size);
7. 解析⾳频包:
1. 获取编码类型:srs_utils_flv_audio_sound_format;
2. 获取⾳频采样率:srs_utils_flv_audio_sound_rate;
3. 获取采样位深:srs_utils_flv_audio_sound_size;
4. 获取声道数:srs_utils_flv_audio_sound_type;
汽车香水瓶5. 获取⾳频包类型:srs_utils_flv_audio_aac_packet_type;
8. 解析视频包:
1. 获取编码类型:srs_utils_flv_video_codec_id;
2. 是否关键帧:srs_utils_flv_video_frame_type;
3. 获取视频包类型:srs_utils_flv_video_avc_packet_type;
9. 解析metadata类型;
我们发现不管是Java层还是C层的接⼝都是提供了类似的思路,其实他们最终调⽤的还是系统的解码框架。
这⾥我们可以根据系统版本来觉得使⽤Java层接⼝和C层接⼝,我们的oarplayer,主要的代码都是在C层实现,所以我们也有限使⽤C层接⼝。
7. ⾳频输出线程
⾳频输出我们使⽤opensl实现,之前⽂章介绍过Android⾳频架构,其实也可以使⽤AAudio或者Oboe。这⾥再简单介绍下opensl es的使⽤。
1. 创建引擎:slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
2. 实现引擎:(*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
3. 获取接⼝:(*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
4. 创建输出混流器:(*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);;
5. 实现混流器:(*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
6. 配置⾳频源:SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
7. 配置Format:SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channel, SL_SAMPLINGRATE_44_1,SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16,SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN};
8. 创建播放器:(*engineEngine)->CreateAudioPlayer(engineEngine,&bqPlayerObject, &audioSrc, &audioSnk,2, ids, req);
9. 实现播放器:(*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
10. 获取播放接⼝:(*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
11. 获取缓冲区接⼝:(*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,&bqPlayerBufferQueue);
12. 注册缓存回调:(*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, oar);
13. 获取⾳量调节器:(*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
14. 缓存回调中不断的从⾳频帧队列读取数据,并写⼊缓存队列:(*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, ctx->buffer,(SLuint32)ctx-
>frame_size);
上⾯就是⾳频播放的opensl es接⼝使⽤介绍。
8. 渲染线程
相⽐较于⾳频播放,视频渲染可能更复杂⼀些,除了opengl引擎创建,opengl线程创建,oarplayer使⽤的是基于⾳频的同步⽅式,所以在视频渲染时还需要考虑⾳视频同步问题。
8.1 OpenGL引擎创建
1. ⽣成buffer:glGenBuffers
2. 绑定buffer:glBindBuffer(GL_ARRAY_BUFFER, model->vbos[0])
3. 设置清屏⾊:glClearColor
4. 创建纹理对象:texture2D
5. 创建着⾊器对象:glCreateShader
6. 设置着⾊器源码:glShaderSource
7. 编译着⾊器源码:glCompileShader
8. 附着着⾊器:glAttachShader
9. 连接着⾊器:glLinkProgram
标志验证网opengl与硬件交互还需要EGL环境,下⾯展⽰EGL初始化流程代码:

本文发布于:2023-07-01 07:59:45,感谢您对本站的认可!

本文链接:https://patent.en369.cn/patent/2/159440.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:视频   播放   播放器   直播   线程   客户端   获取   队列
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 369专利查询检索平台 豫ICP备2021025688号-20 网站地图