From 3302c953ef1a7d0075eaa9555911ed7f3791f4c4 Mon Sep 17 00:00:00 2001 From: kelinlang Date: Fri, 3 Jan 2020 14:31:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 设计/ffmpeg推拉流播放器录像.md | 127 +++++++++++++++++++++++++++++++++ 设计/~$设计.docx | Bin 0 -> 162 bytes 设计/~WRL0003.tmp | 0 设计/工作总结-邓军.md | 37 ++++++++++ 设计/流媒体平台设计.md | 59 +++++++++++++++ 设计/设计.docx | Bin 0 -> 13730 bytes 6 files changed, 223 insertions(+) create mode 100644 设计/ffmpeg推拉流播放器录像.md create mode 100644 设计/~$设计.docx create mode 100644 设计/~WRL0003.tmp create mode 100644 设计/工作总结-邓军.md create mode 100644 设计/流媒体平台设计.md create mode 100644 设计/设计.docx diff --git a/设计/ffmpeg推拉流播放器录像.md b/设计/ffmpeg推拉流播放器录像.md new file mode 100644 index 0000000..30f9931 --- /dev/null +++ b/设计/ffmpeg推拉流播放器录像.md @@ -0,0 +1,127 @@ + +# 流媒体平台 +## 流媒体底层需求 +- 支持第三方接入开发 +- 支持内部产品平台 +- 支持跨平台(linux、windows、Android、ios...) +- 流媒体输入源 + - 视频文件 + - 编码芯片 + - camera + - 网络流:rtmp、rtsp、hls、udp、webrtc... + - 其他源 +- 流媒体输出 + - 网络推流:rtmp、rtsp、hls、udp(ts)、webrtc... + - Ndi + - 本地播放器 + - 视频文件存储 + - hdmi +- 编解码:软编码和硬编码 + - 视频:h264、h265 + - 音频:aac ... +- 流媒体服务器:设备部署,云服务器 +- 编译支持 + - 单独编译:适合独立开发 + - 集成编译:适合集成到产品开发 + +## 中间层 +- Lua framework (kiloview) + - 包装底层sdk,提供应用层接入api +- Java framework + - TODO + +## 应用层 +- 开发方式 + - 接入流媒体底层sdk + - 接入中间层sdk + + +# 流媒体底层设计 +- 独立仓库、独立编译、提高开发和调试效率 +- 设计成sdk,cmake构建跨平台编译系统 +- 提供api接入文件、和接入demo + + + + + + + + + + + + + + + + + + + + + +# 需求 + 1.依据目前代码,底层使用ffmpeg代码,重新编码实现流媒体推拉流,播放,保存,录像磁盘管理功能 + +# 目前代码存在问题 + 1.集成在kiloview整体编译环境,不方便独立调试、测试、跨平台移植、客户定制 + 2.对象继承和组合的选择 + +# 设计构想 + 1.代码独立仓库,不依赖目前工程编译系统,cmake实现跨平台编译,减少后续可能的移植工作量 + 2.代码设计成sdk,提供接入文档,具体业务接入sdk实现业务功能 + 3.使用C++11编码 + + +# 需求分解 +## 支持的视频源 + 1.视频文件 + 2.网络流(rtmp、rtsp、ts、hls) + 3.编码器 + 4.音视频采集设备 + +## 推流 + 1.rtmp、rtsp、ts + 2.可扩展私有协议 + +## 编解码 + 1.软件编解码 + 2.硬件编解码 + +## 录像 + 1.录像保存 + 2.磁盘管理 + +## 播放器 + 1.多视频播放 + +# 设计要求 + 1.功能设计SDK化,给业务层提供接口,应对各种需求 + 2.代码执行效率高,内存占用少 + 3.独立代码仓库,多系统适配(linux,windows,Android,ios),cmake构建跨平台编译 + 4.多通道支持 + +# 实施步骤 + 1.讨论需求 + 2.业务抽象、定义接口,接口文档,审核接口 + 3.编码 + 4.编写测试代码 + 5.测试 + 6.项目接入 + + +# 代码结构 +## 基础库 + 1.日志系统 + 2.线程池 + 3.事件通知 +## 三方库 +## sdk代码 + + + + + + + diff --git a/设计/~$设计.docx b/设计/~$设计.docx new file mode 100644 index 0000000000000000000000000000000000000000..08064cf088b1b6938a763d852f79b9de154e81c3 GIT binary patch literal 162 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 CLVn)> literal 0 HcmV?d00001