FM987一期技术解析

前言

在最近的一段时间里,我做了一个小项目叫做FM987。在我的需求规划里面,这个项目包含了点歌,音频直播,聊天等功能,目前只完成了一期的功能(点歌和音频直播)。如题所述,这篇文章主要介绍一期功能中,一些我觉得比较有趣也比较有分享价值的东西。

整体方案概述

为了方便大家更好的理解,所以我打算从整体架构开始介绍,然后再讲到具体细节。

音频直播

对于FM987来说,最核心的功能就是实时听歌功能,所以选择音频直播方案就很顺理成章了。

最简单的架构图

对于音频直播而言,最简单的架构图可能就是下面这张图了: 音频源上传音频,通过服务器转发给听众。


直播基本结构图

自定义音频源的直播

在FM987的需求中,大家听到的歌曲是听众自己点歌的,并不像传统方案中,由客户端录音然后推流到服务器。所以为了满足点歌直播的需求,我们就需要在服务器实现一个自定义音频源,自定义音频源可以将用户点的歌变成音频数据发送听众。在我的实现方案中,将这个音频源称为Player,下面是加入Player后的架构图。


Player的直播架构图

更详细的架构图

自定义音频源是FM987最核心的东西,也是最区别于其他直播方案的东西,其他的东西都是基于这个点进行拓展,包括如何点歌,如何获取音频文件,怎么解析文件等。下面是更详细的架构图,大家可以看下。


整体架构图

当然,从架构图上就看出,现在fm987的架构还不是很合理,例如player干了太多事情,可以尝试将一些东西抽离出来。

网易云和QQ音乐的下载接口解析

FM987可以让用户进行搜歌、点歌。我最初是选择了网易云、QQ音乐和虾米作为点歌的数据源,不过虾米接口的验证码机制让我放弃了它,所以现在只接入了网易云和QQ音乐,并且只有支持免费歌曲。不过这两家的开发肯定也知道有很多人会拉取他们的数据,所以他们在接口的防调用上做了一些保护措施,特别是获取下载地址接口。

网易云的下载接口加密

网易云的下载接口有两个参数:paramsencSecKey,例如:

网易云下载接口参数

看到这两个参数的值,可能大家就明白了,我为什么要特别介绍这个接口了。
params的值的形成流程是这样的:
params的形成

这个加密方式应该是我见过最复杂的了,而且有几个点很有意思:

  1. Json序列化,正常这部分都是存到map中再序列化,而大部分map都是无序的,但是网易云的后台验证中,会要求几个参数必须按照一定的顺序进行序列化
  2. 随机生成的16位key,如果不知道这个值,后端是无法对参数进行解密的,而encSecKey就是这个key加密后的值

encSecKey的形成过程:网上有人说这个值是通过对key做rsa算法加密后得到了,但是我用常规的rsa加密后无法通过验证,所以我觉得这个rsa算法可能是网易云自己设计的,当然也可能是我对于rsa的了解不够吧。下面附上java的加密代码:

private static String neteaseRsaEncrypt(String src, String exponentStr, String modulusStr) throws Exception {
        byte[] bytes = src.getBytes("UTF-8");
        ArrayUtils.reverse(bytes);
        String tempText = HexUtils.toHexString(bytes);
        BigInteger biText = new BigInteger(tempText, 16);
        BigInteger biExponent = new BigInteger(exponentStr, 16);
        BigInteger biModulus = new BigInteger(modulusStr, 16);
        BigInteger bitRet = biText.modPow(biExponent, biModulus);
        return neteaseFill(bitRet.toString(16), 256);
    }

    private static String neteaseFill(String str, int length) {
        StringBuilder stringBuilder = new StringBuilder(str);
        while (stringBuilder.length() < length) {
            stringBuilder.insert(0, "0");
        }
        return stringBuilder.toString();
    }

QQ音乐的下载接口

QQ音乐他们的下载接口没有像网易云那么复杂,只是增加了一个获取token的过程,并且这个token有时效性。


QQ音乐获取下载地址的流程

基于flv的音频直播方案

在这个直播时代,直播方案有很多种,下面我会介绍下选择flv的原因,并且怎么实现他的流服务器的。

各种直播方案的对比

传输协议 播放器 延迟
RTMP Flash 1s
HTTP-FLV Video 1s
HLS Video 10S

RTMP对我来说太重了,没必要。而HLS的延迟我在几年前刚接触的时候就有体会了,直接就被否决掉了。所以就剩下http-flv,而且B站的flv.js挺出名的,趁这个机会也可以用用看。

HTTP-FLV的实现方案

HTTP-FLV分为http和flv,http是获取数据的方式,flv是数据的格式,所以下面先介绍下flv协议,然后再介绍如何通过http来获取flv的流数据。

flv协议

我这里就不介绍flv的背景,有兴趣的同学可以自行百科下,主要说下flv协议。

flv协议

从flv的协议就能明白,flv协议是一种很适合直播流的协议。当听众直播间后,我们只需要先给听众发送一个flv header的数据块,然后就可以只发送tag数据块了,而每一个tag都是相对独立的,可以让我们对于播放的内容有很大的灵活性。在FM987中,我只需要将音频文件转码为flv文件,然后将文件解析为一个个tag数据块,就可以根据需要发送给听众进行播放了。

http流

在http1.1协议中,有一种叫做chunked transter encoding的传输编码方式,它允许服务器将要发送的数据分成多个部分,然后逐个发送给客户端。这个传输方式和flv简直是天作之合。我们可以每一个tag的数据都为一个块发送给客户端.下面是我发送数据的代码

FLVPacket packet = flvTag.getFlvPacket();
byte[] data = packet.getData();
byte[] chunkedHeader = (Integer.toHexString(data.length) + "\r\n").getBytes();
byte[] chunkedFooter = "\r\n".getBytes();
socket.getOutputStream().write(chunkedHeader);
socket.getOutputStream().write(data);
socket.getOutputStream().write(chunkedFooter);
socket.getOutputStream().flush();

结尾

在上面的技术解析中,我将一些我个人觉得有点意思的东西介绍下,知识点比较杂,也比较零散。很多东西也没有细讲,更多是提供一种思路,希望对大家有帮助吧。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • <转> 一、个人见解(直播难与易) 直播难:个人认为要想把直播从零开始做出来,绝对是牛逼中的牛逼,大牛中的大牛,因...
    XLsn0w阅读 1,558评论 0 5
  • 十刀阅读 493评论 0 2
  • 最近在项目中遇到一个需要做定时执行任务转储数据,在网上无意间发现了Quartz这个作业调度框架。Quartz是一个...
    LmeJojo阅读 709评论 0 2
  • 人生总有那么一些日子,发生的看似都是一些微不足道的小事,却已在你的记忆深处留下了波澜,而在那些孤独的时刻,...
    法兰西稀粥阅读 942评论 0 2
  • 1, Linux进阶 2,高于入门级的统计学知识,以及一门统计语言,比如 R 3,Python,进阶使用C语言。 ...
    这是李金辉呀阅读 240评论 0 0