分布式多媒体互动应用
分布式多媒体互动应用 #HarmonyOS挑战赛第二期#分布式多媒体互动应用
一、简介
分布式多媒体互动应用是基于#HarmonyOS挑战赛第二期# 的挑战要求结合HarmonyOS媒体组件和分布式能力等,构建一款分布式多媒体互动的应用。应用功能包括滑动切换视频功能、评论功能(评论数据使用了分布式数据库实现了分布式和持久化)、分布式流转功能、远程控制播放和发弹幕互动功能、分布式流转后同步观看并发弹幕互动功能。
本应用可满足个人娱乐、家庭互助、朋友娱乐社交等场景,例如以下使用场景:
- 自己观看本地视频,并发送评论或者弹幕;
- 把手机里视频流转到智慧屏上给小孩或者老人观看,并远程控制播放,同时可以发送弹幕互动;
- 在视频流转后,也可同步观看同一视频,并发送评论、弹幕互动。
二、功能介绍
- 滑动切换视频功能
动画演示:
核心代码展示:
/**
* 增加视频滑动切换功能
*/
private void changeVideo()
{
player.setDraggedListener(
Component.DRAG_HORIZONTAL_VERTICAL,
new Component.DraggedListener() {
@Override
public void onDragDown(Component component, DragInfo dragInfo)
{
HiLog.info(VIDEO_BOX_LABEL, "VideoView onDragDown");
}
@Override
public void onDragStart(Component component, DragInfo dragInfo)
{
}
@Override
public void onDragUpdate(Component component, DragInfo dragInfo)
{
currentPlayingIndex++;
int size = videoService.getAllVideoInfo().getDetail().size();
if (currentPlayingIndex >= size)
{
currentPlayingIndex = 0;
}
playbackNext(videoService.getVideoInfoByIndex(currentPlayingIndex));
}
@Override
public void onDragEnd(Component component, DragInfo dragInfo)
{
}
@Override
public void onDragCancel(Component component, DragInfo dragInfo)
{
}
}
);
}
参考资料:官方工程模板VideoPlayerAbility
- 评论分布式持久化功能
动画演示:
核心代码展示:
/**
* Initializing Database Management
*/
private void initDbManager()
{
kvManager = createManager();
singleKvStore = createDb(kvManager);
subscribeDb(singleKvStore);
}
/**
* Create a distributed database manager instance
*
* @return database manager
*/
private KvManager createManager()
{
KvManager manager = null;
try
{
KvManagerConfig config = new KvManagerConfig(this);
manager = KvManagerFactory.getInstance().createKvManager(config);
}
catch (KvStoreException exception)
{
}
return manager;
}
/**
* Creating a Single-Version Distributed Database
*
* @param manager Database management
* @return SingleKvStore
*/
private SingleKvStore createDb(KvManager manager)
{
SingleKvStore kvStore = null;
try
{
Options options = new Options();
options.setCreateIfMissing(true).setEncrypt(false).setKvStoreType(KvStoreType.SINGLE_VERSION);
kvStore = manager.getKvStore(options, STORE_ID);
}
catch (KvStoreException exception)
{
}
return kvStore;
}
/**
* Subscribing to All (Currently, Remote) Data Change Notifications of a Single-Version Distributed Database
*
* @param kvStore Data operation
*/
private void subscribeDb(SingleKvStore kvStore)
{
KvStoreObserver kvStoreObserverClient = new KvStoreObserverClient();
kvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_REMOTE, kvStoreObserverClient);
}
/**
* Receive database messages
*
* @since 2021-01-06
*/
private class KvStoreObserverClient implements KvStoreObserver
{
@Override
public void onChange(ChangeNotification notification)
{
getUITaskDispatcher().asyncDispatch(() ->
{
queryData();
ToastUtils.showTips(getContext(), "同步成功", 0);
});
}
}
/**
* 查询评论列表数据
*/
private void queryData()
{
List<Entry> entryList = singleKvStore.getEntries("");
selectNews.clear();
try
{
for (Entry entry : entryList)
{
selectNews.add(new NewsInfo(entry.getKey(), entry.getValue().getString()));
}
}
catch (KvStoreException exception)
{
}
newsListProvider.notifyDataChanged();
}
参考资料:官方工程模板NewsAbility和分布式数据服务的单版本分布式数据库类型KvStoreType.SINGLE_VERSION
- 分布式流转远程控制功能
动画演示:
核心代码展示:
//分布式流转远程遥控
@Override
public void onClick(Component component)
{
switch (component.getId())
{
case ResourceTable.Id_app_bar_back:
hide(true);
break;
case ResourceTable.Id_control_episodes_num:
case ResourceTable.Id_control_all_episodes:
episodesDialog.setVisibility(VISIBLE);
break;
case ResourceTable.Id_control_play:
remoteControllerListener.sendControl(ControlCode.PLAY.getCode(), "");
break;
case ResourceTable.Id_control_backword:
remoteControllerListener.sendControl(ControlCode.BACKWARD.getCode(), "");
break;
case ResourceTable.Id_control_forward:
remoteControllerListener.sendControl(ControlCode.FORWARD.getCode(), "");
break;
case ResourceTable.Id_control_voice_up:
remoteControllerListener.sendControl(ControlCode.VOLUME_ADD.getCode(), "");
break;
case ResourceTable.Id_control_voice_down:
if (getDialogVisibility())
{
remoteControllerListener.sendControl(ControlCode.VOLUME_REDUCED.getCode(), "");
}
break;
case ResourceTable.Id_comment_button:
if (commentTextField != null)
{
String text = commentTextField.getText();
if (text != null && !text.equals(""))
{
remoteControllerListener.sendControl(ControlCode.COMMENT.getCode(), text);
ToastUtils.showTips(getContext(), "弹幕发送成功:\r\n" + text, 0);
commentTextField.setText("");
}
else
{
ToastUtils.showTips(getContext(), "内容为空,请输入弹幕!", 0);
}
}
break;
default:
break;
}
}
- 分布式弹幕功能
动画演示:
核心代码展示:
//被流转端接收远程控制信息
class MyCommonEventSubscriber extends CommonEventSubscriber
{
MyCommonEventSubscriber(CommonEventSubscribeInfo info)
{
super(info);
}
@Override
public void onReceiveEvent(CommonEventData commonEventData)
{
HiLog.info(LABEL, "onReceiveEvent.....");
Intent intent = commonEventData.getIntent();
int controlCode = intent.getIntParam(Constants.KEY_CONTROL_CODE, 0);
String extras = intent.getStringParam(Constants.KEY_CONTROL_VALUE);
if (controlCode == ControlCode.PLAY.getCode())
{
if (videoBox.isPlaying())
{
videoBox.pause();
}
else if (!videoBox.isPlaying() && !needResumeStatus)
{
videoBox.start();
}
else
{
HiLog.error(LABEL, "Ignoring the case with player status");
}
}
else if (controlCode == ControlCode.SEEK.getCode())
{
videoBox.seekTo(videoBox.getDuration() * Integer.parseInt(extras) / 100);
}
else if (controlCode == ControlCode.FORWARD.getCode())
{
videoBox.seekTo(videoBox.getCurrentPosition() + Constants.REWIND_STEP);
}
else if (controlCode == ControlCode.BACKWARD.getCode())
{
videoBox.seekTo(videoBox.getCurrentPosition() - Constants.REWIND_STEP);
}
else if (controlCode == ControlCode.VOLUME_ADD.getCode())
{
videoBox.setVolume(Constants.VOLUME_STEP);
}
else if (controlCode == ControlCode.VOLUME_REDUCED.getCode())
{
videoBox.setVolume(-Constants.VOLUME_STEP);
}
else if (controlCode == ControlCode.SWITCH_SPEED.getCode())
{
videoBox.setPlaybackSpeed(Float.parseFloat(extras));
}
else if (controlCode == ControlCode.SWITCH_RESOLUTION.getCode())
{
long currentPosition = videoBox.getCurrentPosition();
int resolutionIndex = Integer.parseInt(extras);
VideoInfo videoInfo = videoInfoService.getVideoInfoByIndex(currentPlayingIndex);
videoBox.pause();
videoBox.setVideoPath(videoInfo.getResolutions().get(resolutionIndex).getUrl());
videoBox.setPlayerOnPreparedListener(
() ->
{
videoBox.seekTo(currentPosition);
videoBox.start();
});
}
else if (controlCode == ControlCode.SWITCH_VIDEO.getCode())
{
videoBox.pause();
currentPlayingIndex = Integer.parseInt(extras);
VideoInfo videoInfo = videoInfoService.getVideoInfoByIndex(currentPlayingIndex);
videoBox.setVideoPathAndTitle(videoInfo.getResolutions().get(0).getUrl(), videoInfo.getVideoDesc());
videoBox.setPlayerOnPreparedListener(() -> videoBox.start());
}
else if (controlCode == ControlCode.STOP_CONNECTION.getCode())
{
terminate();
}
else if (controlCode == ControlCode.COMMENT.getCode())
{
if (textAutoScrolling != null)
{
textAutoScrolling.setText(extras);
textAutoScrolling.startAutoScrolling();
}
}
else
{
HiLog.error(LABEL, "Ignoring the case with control code");
}
}
}
- 分布式流转后同步观看并发弹幕互动功能
动画演示:
核心代码展示:
// 发送按钮,进行评论/弹幕
Text textAutoScrolling = (Text) findComponentById(ResourceTable.Id_text_auto_scrolling);
if (textAutoScrolling != null)
{
textAutoScrolling.setTruncationMode(Text.TruncationMode.AUTO_SCROLLING);
textAutoScrolling.setAutoScrollingCount(Text.AUTO_SCROLLING_FOREVER);
textAutoScrolling.startAutoScrolling();
}
button.setClickedListener(listener ->
{
String text = commentTextField.getText();
if (text != null && !text.equals(""))
{
if (isDanmuFlag)
{
if (remoteController != null)
{
remoteController.sendDanmu(text);
}
if (textAutoScrolling != null)
{
textAutoScrolling.setText(text);
textAutoScrolling.startAutoScrolling();
}
}
NewsInfo newsInfo = new NewsInfo();
newsInfo.setTitle(text);
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd a hh:mm");
String date = sdf1.format(new Date(System.currentTimeMillis()));
newsInfo.setTime(date);
newsInfo.setType("自己:");
selectNews.add(newsInfo);
singleKvStore.putString(text, date);
queryData();
newsListContainer.scrollToCenter(selectNews.size());
commentTextField.setText("");
}
});
getUITaskDispatcher().delayDispatch(this::queryData, DELAY_MS);
三、工程源码
代码工程和相关视频演示、gif演示、程序截图已放到了Gitee指定的代码仓里-代码仓,欢迎大家指导指导,有兴趣一起开源开发!
四、总结
本次开发时间比较有限,但是在华为开发者联盟论坛里有大量HarmonyOS应用开发的工程例子和DevEco Studio 的模板,非常方便地实现了丰富的功能。例如本应用就是在Video PlayerAbility模板下生成工程,使用了分布式数据库单版本分布式数据库类型KvStoreType.SINGLE_VERSION,同时实现了数据的分布式和持久化。本应用的使用场景重点在于视频分享后可能进行互动,因为时间关系,还有如下改进方向:
- 目前播放视频为内置视频,后面可增加自定义播放本地视频;
- 目前弹幕效果比较单一,可考虑接入一些第三方弹幕库进行效果丰富;
- 目前是以评论和弹幕进行互动,都属于文字类的,可接入语音模块,已了解到官方有相关工程例子,后面?可方便实现;
- 目前分布式的流转是Phone端到TV端,而且是单向的,后面可以增加Phone端到Phone端、Tablet端等,同时可两端相互通讯。
五、个人感想
本人有幸参加了HDC2019和HDC2020,同步见证着鸿蒙的诞生,到现在茁壮成长,Harmony OS开发已经成为了我们开发者中不可忽视的领域,也确实值得深入研究,融入鸿蒙生态之中来创造个人更大的价值。躬身入局,才是解决困境的最佳途径。期待HDC2021!鸿蒙可期!华为可期!
最后,预祝HDC2021完满成功,华为开发者联盟越来越强大!
楼主太厉害了,可以分享源码参考一下吗?
这里没有提供具体的HTML内容,仅有一个文本和一个链接。根据你的要求,转换后的Markdown文档如下:
这里没有提供具体的HTML内容,仅有一个文本和一个链接。需要的话再更新一下,
这个显示访问受限,楼主可以发一下邮箱alaluminium@outlook.com吗?做鸿蒙毕设想学习一下,感谢,
可以,毕设加油^^,
666,666,加油!
感谢大神,让我见识了鸿蒙分布式的厉害,666!
6666666666666
thx~~~~~~~~
分布式多媒体互动应用是一种基于网络的多媒体系统,支持多个用户在不同地点实时交互和共享多媒体内容。其核心技术包括分布式计算、流媒体传输、实时通讯协议(如WebRTC)和数据同步机制。通过负载均衡和内容分发网络(CDN),系统能够高效处理大规模并发请求,确保低延迟和高可靠性。典型应用场景包括在线教育、远程会议、协同设计和多人在线游戏。其架构通常分为客户端、服务器和存储层,采用微服务架构以提高可扩展性和维护性。