歌词同步实现流程

1 功能实现流程图

目前运用最广的歌词文件LRC文件内容如下。

[00:02.37] 即构KTV

[00:03.12]

[00:03.80] 作词:即构

[00:04.55] 作曲:即构

[00:05.18] 演唱:即构

[00:06.43] 出品:深圳即构科技

这种格式文件小,容易解析,歌词的精度控制到一行。

当观众、合唱、主唱都存在此歌词文件时,此时只需要知道当前音乐进度时间戳,就能达到歌词同步。

即构 SDK 支持将非媒体信息注入媒体流中。歌词信息和音视频信息是在同一个媒体通道传输的,这样可以保障歌词和音视频同步。歌词进度由主唱端发送,观众跟合唱端接收到时间戳后,再根据时间戳把关键歌词行高亮。达到歌词同步展示的效果。

具体实现流程图请参考:

149090904_1_2018120810084459

内部约定主唱端与观众、合唱的歌词同步信息通信格式即可。

2 接收歌词时间戳数据

即构 SDK 拉流解码提取出媒体次要信息,会将其通过 onRecvMediaSideInfo 回调给应用开发者。合唱者、观众需要关注这个回调接口。

2.1 设置回调,接收媒体次要信息

ZEGO SDK 提供了相关接口,用于设置回调,接收媒体次要信息。

/**

* 设置回调,接收媒体次要信息

*/

mZegoLiveRoom.setZegoMediaSideCallback(new IZegoMediaSideCallback() {

@Override

public void onRecvMediaSideInfo(String streamID, ByteBuffer byteBuffer, int dataLen) {

}

});

其中,当用户端收到媒体次要信息时,SDK 会调用之。

2.2 实现回调函数,处理媒体次要信息

媒体次要信息会作为 onRecvMediaSideInfo 的入参传递进去,开发者需要在该函数内,根据业务要求,处理媒体次要信息。

主唱向观众、合唱端发送的数据格式为时间戳。KTV App 中处理媒体次要信息的示例代码如下:

/**

* 设置回调,接收媒体次要信息

*/

mZegoLiveRoom.setZegoMediaSideCallback(new IZegoMediaSideCallback() {

@Override

public void onRecvMediaSideInfo(String streamID, ByteBuffer byteBuffer, int dataLen) {

if (dataLen == 0) {

return;

}

int offset = 4;

// 偏移offset字节转换成int

int timeStamp = ((byteBuffer.get(offset) & 0xff) | ((byteBuffer.get(offset + 1) & 0xff) << 8) |

((byteBuffer.get(offset + 2) & 0xff) << 16) | (byteBuffer.get(offset + 3) & 0xff) << 24);

// 此处拿到时间戳数据 timeStamp

// 拿到timeStamp后根据时间戳的值展示歌词信息

}

});

当拿到timeStamp后,解析歌词展示timeStamp时间段的歌词数据就OK了。

3 主唱端发送歌词信息

3.1 发送媒体次要信息

即构 SDK 提供了相关接口,用于发送媒体次要信息。在 KTV 场景中,歌词信息就作为媒体次要信息注入媒体流中,连同音视频数据一同发送到拉流端。观众、合唱者等拉流端接受到媒体次要信息后,得到应用进度时间戳。

如果要发送媒体次要信息,首先需要开启媒体次要信息开关。接口如下所示:

ZegoLiveRoom

/**

发送媒体次要信息开关

@param start 开启媒体次要信息传输,true 开启媒体次要信息传输, false 关闭媒体次要信息传输。start 为 true 时,onlyAudioPublish 开关才有效

@param onlyAudioPublish 是否纯音频直播,true 纯音频直播,不传输视频数据,false 音视频直播,传输视频数据。默认为 false。如果本次只有音频直播,必须将 onlyAudioPublish 置为 true,此时将会由音频来驱动次要信息的传输,同时忽略视频流传输

@discussion 初始化 SDK 后,开始推流前调用。

*/

(ZegoLiveRoom)setMediaSideFlags(boolean start , boolean onlyAudioPublish);

开启媒体次要信息开关后,才能使用以下接口进行发送媒体次要信息。

ZegoLiveRoom

/**

发送媒体次要信息

@param inData 需要传输的音视频次要信息数据,外部输入

@param dataLen 传入的 inData 总长度,不能大于 1000 Bytes

@param packet 是否外部已经打包好包头,true 已打包, false 未打包。如果没有特殊情况,建议用户使用内部打包

@discussion 主播端开启媒体次要信息开关,开始推流后调用。调用此 API 发送媒体次要信息后,观众端在

(ZegoLiveRoom)setZegoMediaSideCallback() 设置的回调中获取媒体次要信息。不需要发送媒体次要信息时,可调用 (ZegoLiveRoom)setMediaSideFlags(false,false) 关闭通道

*/

(void)sendMediaSideInfo(ByteBuffer inData, int dataLen, boolean packet);

3.2 使用发送媒体次要信息接口,如何进行媒体次要信息发送

媒体次要信息会作为 inData 的参传入到SDK,开发者调用该函数,根据业务要求,传入媒体次要信息。

演示 Demo 中发送媒体次要信息的示例代码如下:

int timeStamp = // 获取播放进度时间戳

byte[] mediaSide = new byte[4];

mediaSide[0] = (byte) (timeStamp & 0xff); // 低位(右边)的8个bit位

mediaSide[1] = (byte) ((timeStamp >> 8) & 0xff); //第二个8 bit位

mediaSide[2] = (byte) ((timeStamp >> 16) & 0xff); //第三个 8 bit位

mediaSide[3] = (byte) ((timeStamp >> 24) & 0xff); //第4个 8 bit位

ByteBuffer inData = ByteBuffer.allocateDirect(mediaSide.length);

inData.mediaSideInfoBuf.put(mediaSide, 0, mediaSide.length);

inData.mediaSideInfoBuf.flip();

mZegoLiveRoom.sendMediaSideInfo(inData, mediaSide.length, false);

timeStamp,是当前播放的进度时间戳,需要通过播放器SDK MediaPlayer播放器的函数,去获取该伴奏播放的进度时间戳。

关于媒体次要信息功能的详细说明,可参考文档:媒体次要信息

3.3 使用MediaPlayer播放器拿到当前播放的时间戳数据

知道怎么发送媒体次要信息了,接下来使用ZEGO SDK MediaPlayer提供的函数拿到时间戳

示例代码如下:

ZegoMediaPlayer player = new ZegoMediaPlayer();

player.init(ZegoMediaPlayer.PlayerTypeAux);

// 通过该函数获取到播放伴奏进度时间戳

int timeStamp = player.getCurrentDuration();

拿到timeStamp 后,主唱端需要做一个定时器循环去取这个时间戳使用SDK媒体次要信息接口发送出去。

每秒循环取一次,直到该首歌播放完毕, 让观众、合唱歌词展示达到同步状态。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐