0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

OpenHarmony 3.2 Beta多媒体系列:音视频播放框架

电子发烧友开源社区 来源:未知 2022-11-23 09:40 次阅读

巴延兴

深圳开鸿数字产业发展有限公司

资深OS框架开发工程师

以下内容来自嘉宾分享,不代表开放原子开源基金会观点

一、简介

媒体子系统为开发者提供一套接口,方便开发者使用系统的媒体资源,主要包含音视频开发、相机开发、流媒体开发等模块。每个模块都提供给上层应用对应的接口,本文会对音视频开发中的音视频播放框架做一个详细的介绍。

二、目录

foundation/multimedia/media_standard
├── frameworks                        #框架代码
│ ├── js
│ │ ├── player
│ ├── native
│ │ ├── player                    #native实现
│ └── videodisplaymanager         #显示管理
│     ├── include
│     └── src
├── interfaces
│ ├── inner_api                     #内部接口
│ │ └── native
│ └── kits                          #外部JS接口
├── sa_profile                        #服务配置文件
└── services
  ├── engine                        #engine代码
  │ └── gstreamer
  ├── etc                           #服务配置文件
  ├── include                       #头文件
  └── services
    ├── sa_media                  #media服务
    │ ├── client                #media客户端
    │ ├── ipc                   #media ipc调用
    │ └── server                #media服务端
    ├── factory                   #engine工厂
    └── player                    #player服务
     ├── client                 #player客户端
      ├── ipc                    #player ipc调用
└──server#player服务端

三、播放的总体流程

ab3cba20-6acf-11ed-8abf-dac502259ad0.png  

四、Native接口使用

OpenHarmony系统中,音视频播放通过N-API接口提供给上层JS调用,N-API相当于是JS和Native之间的桥梁,在OpenHarmony源码中,提供了C++直接调用的音视频播放例子,在foundation/multimedia/player_framework/test/nativedemo/player目录中。
void PlayerDemo::RunCase(const string &path)
{
    player_ = OHOS::CreatePlayer();
    if (player_ == nullptr) {
        cout << "player_ is null" << endl;
        return;
    }
    RegisterTable();
    std::shared_ptr cb = std::make_shared();
    cb->SetBufferingOut(SelectBufferingOut());


    int32_t ret = player_->SetPlayerCallback(cb);
    if (ret != 0) {
        cout << "SetPlayerCallback fail" << endl;
    }
    if (SelectSource(path) != 0) {
        cout << "SetSource fail" << endl;
        return;
    }
    sptr producerSurface = nullptr;
    producerSurface = GetVideoSurface();
    if (producerSurface != nullptr) {
        ret = player_->SetVideoSurface(producerSurface);
        if (ret != 0) {
            cout << "SetVideoSurface fail" << endl;
        }
    }
    SetVideoScaleType();
    if (SelectRendererMode() != 0) {
        cout << "set renderer info fail" << endl;
    }
    ret = player_->PrepareAsync();
    if (ret !=  0) {
        cout << "PrepareAsync fail" << endl;
        return;
    }
    cout << "Enter your step:" << endl;
    DoNext();
}
首先根据RunCase可以大致了解一下播放音视频的主要流程,创建播放器,设置播放源,设置回调方法(包含播放过程中的多种状态的回调),设置播放显示的Surface,这些准备工作做好之后,需要调用播放器的PrepareASync方法,这个方法完成后,播放状态会变成Prepared状态,这时就可以调用播放器的play接口,进行音视频的播放了。 RegisterTable()方法中,将字符串和对应的方法映射到Map中,这样后续的DoNext会根据输入的命令,来决定播放器具体的操作。
void PlayerDemo::DoNext()
{
    std::string cmd;
    while (std::cin, cmd)) {
        auto iter = playerTable_.find(cmd);
        if (iter != playerTable_.end()) {
            auto func = iter->second;
            if (func() != 0) {
                cout << "Operation error" << endl;
            }
            if (cmd.find("stop") != std::npos && dataSrc_ != nullptr) {
                dataSrc_->Reset();
            }
            continue;
        } else if (cmd.find("quit") != std::npos || cmd == "q") {
            break;
        } else {
            DoCmd(cmd);
            continue;
        }
    }
}


void PlayerDemo::RegisterTable()
{
    (void)playerTable_.emplace("prepare", std::bind(&Player::Prepare, player_));
    (void)playerTable_.emplace("prepareasync", std::bind(&Player::PrepareAsync, player_));
    (void)playerTable_.emplace("", std::bind(&Player::Play, player_)); // ENTER -> play
    (void)playerTable_.emplace("play", std::bind(&Player::Play, player_));
    (void)playerTable_.emplace("pause", std::bind(&Player::Pause, player_));
    (void)playerTable_.emplace("stop", std::bind(&Player::Stop, player_));
    (void)playerTable_.emplace("reset", std::bind(&Player::Reset, player_));
    (void)playerTable_.emplace("release", std::bind(&Player::Release, player_));
    (void)playerTable_.emplace("isplaying", std::bind(&PlayerDemo::GetPlaying, this));
    (void)playerTable_.emplace("isloop", std::bind(&PlayerDemo::GetLooping, this));
    (void)playerTable_.emplace("speed", std::bind(&PlayerDemo::GetPlaybackSpeed, this));
}
以上的DoNext方法中核心的代码是func()的调用,这个func就是之前注册进Map中字符串对应的方法,在RegisterTable方法中将空字符串""和"play"对绑定为Player::Play方法,默认不输入命令参数时,是播放操作。

五、调用流程

ab47b74a-6acf-11ed-8abf-dac502259ad0.jpgab6a3888-6acf-11ed-8abf-dac502259ad0.jpg    

左右滑动查看更多

本段落主要针对媒体播放的框架层代码进行分析,所以在流程中涉及到了IPC调用相关的客户端和服务端,代码暂且分析到调用gstreamer引擎。 首先Sample通过PlayerFactory创建了一个播放器实例(PlayerImpl对象),创建过程中调用Init函数。
int32_t PlayerImpl::Init()
{
    playerService_ = MediaServiceFactory::GetInstance().CreatePlayerService();
    CHECK_AND_RETURN_RET_LOG(playerService_ != nullptr, MSERR_UNKNOWN, "failed to create player service");
    return MSERR_OK;
}
MediaServiceFactory::GetInstance()返回的是MediaClient对象,所以CreateplayerService函数实际上是调用了MediaClient对应的方法。
std::shared_ptr MediaClient::CreatePlayerService()
{
    std::lock_guard lock(mutex_);
    if (!IsAlived()) {
        MEDIA_LOGE("media service does not exist.");
        return nullptr;
    }


    sptr object = mediaProxy_->GetSubSystemAbility(
        IStandardMediaService::MEDIA_PLAYER, listenerStub_->AsObject());
    CHECK_AND_RETURN_RET_LOG(object != nullptr, nullptr, "player proxy object is nullptr.");


    sptr playerProxy = iface_cast(object);
    CHECK_AND_RETURN_RET_LOG(playerProxy != nullptr, nullptr, "player proxy is nullptr.");


    std::shared_ptr player = PlayerClient::Create(playerProxy);
    CHECK_AND_RETURN_RET_LOG(player != nullptr, nullptr, "failed to create player client.");


    playerClientList_.push_back(player);
    return player;
}
这个方法中主要通过PlayerClient::Create(playerProxy)方法创建了PlayerClient实例,并且将该实例一层层向上传,最终传给了PlayerImpl的playerService_变量,后续对于播放器的操作,PlayerImpl都是通过调用PlayerClient实例实现的。
int32_t PlayerImpl::Play()
{
    CHECK_AND_RETURN_RET_LOG(playerService_ != nullptr, MSERR_INVALID_OPERATION, "player service does not exist..");
    MEDIA_LOGW("KPI-TRACE: PlayerImpl Play in");
    return playerService_->Play();
}


int32_t PlayerImpl::Prepare()
{
    CHECK_AND_RETURN_RET_LOG(playerService_ != nullptr, MSERR_INVALID_OPERATION, "player service does not exist..");
    MEDIA_LOGW("KPI-TRACE: PlayerImpl Prepare in");
    return playerService_->Prepare();
}


int32_t PlayerImpl::PrepareAsync()
{
    CHECK_AND_RETURN_RET_LOG(playerService_ != nullptr, MSERR_INVALID_OPERATION, "player service does not exist..");
    MEDIA_LOGW("KPI-TRACE: PlayerImpl PrepareAsync in");
    return playerService_->PrepareAsync();
}
对于PlayerImpl来说,playerService_指向的PlayerClient就是具体的实现,PlayerClient的实现是通过IPC的远程调用来实现的,具体地是通过IPC中的proxy端向远端服务发起远程调用请求。 我们以播放Play为例:
int32_t PlayerClient::Play()
{
    std::lock_guard lock(mutex_);
    CHECK_AND_RETURN_RET_LOG(playerProxy_ != nullptr, MSERR_NO_MEMORY, "player service does not exist..");
    return playerProxy_->Play();
}
int32_t PlayerServiceProxy::Play()
{
    MessageParcel data;
    MessageParcel reply;
    MessageOption option;


    if (!data.WriteInterfaceToken(PlayerServiceProxy::GetDescriptor())) {
        MEDIA_LOGE("Failed to write descriptor");
        return MSERR_UNKNOWN;
    }


    int error = Remote()->SendRequest(PLAY, data, reply, option);
    if (error != MSERR_OK) {
        MEDIA_LOGE("Play failed, error: %{public}d", error);
        return error;
    }
    return reply.ReadInt32();
}
proxy端发送调用请求后,对应的Stub端会在PlayerServiceStub::OnRemoteRequest接收到请求,根据请求的参数进行对应的函数调用。播放操作对应的调用Stub的Play方法。
int32_t PlayerServiceStub::Play()
{
    MediaTrace Trace("binder::Play");
    CHECK_AND_RETURN_RET_LOG(playerServer_ != nullptr, MSERR_NO_MEMORY, "player server is nullptr");
    return playerServer_->Play();
}
这里最终是通过playerServer_调用Play函数。playerServer_在Stub初始化的时候通过PlayerServer::Create()方式来获取得到。也就是PlayerServer。
std::shared_ptr PlayerServer::Create()
{
    std::shared_ptr server = std::make_shared();
    CHECK_AND_RETURN_RET_LOG(server != nullptr, nullptr, "failed to new PlayerServer");


    (void)server->Init();
    return server;
}
最终我们的Play调用到了PlayerServer的Play()。在媒体播放的整个过程中会涉及到很多的状态,所以在Play中进行一些状态的判读后调用OnPlay方法。这个方法中发起了一个播放的任务。
int32_t PlayerServer::Play()
{
    std::lock_guard lock(mutex_);


    if (lastOpStatus_ == PLAYER_PREPARED || lastOpStatus_ == PLAYER_PLAYBACK_COMPLETE ||
        lastOpStatus_ == PLAYER_PAUSED) {
        return OnPlay();
    } else {
        MEDIA_LOGE("Can not Play, currentState is %{public}s", GetStatusDescription(lastOpStatus_).c_str());
        return MSERR_INVALID_OPERATION;
    }
}


int32_t PlayerServer::OnPlay()
{
    auto playingTask = std::make_shared>([this]() {
        MediaTrace::TraceBegin("PlayerServer::Play", FAKE_POINTER(this));
        auto currState = std::static_pointer_cast(GetCurrState());
        (void)currState->Play();
    });


    int ret = taskMgr_.LaunchTask(playingTask, PlayerServerTaskType::STATE_CHANGE);
    CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, ret, "Play failed");


    lastOpStatus_ = PLAYER_STARTED;
    return MSERR_OK;
}
在播放任务中调用了PlayerServer::Play()
int32_t PlayerServer::Play()
{
    return server_.HandlePlay();
}
在Play里面直接调用PlayerServer的HandlePlay方法,HandlePlay方法通过playerEngine_调用到了gstreamer引擎,gstreamer是最终播放的实现。
int32_t PlayerServer::HandlePlay()
{
    int32_t ret = playerEngine_->Play();
    CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Engine Play Failed!");


    return MSERR_OK;
}

六、总结

本文主要对OpenHarmony 3.2 Beta多媒体子系统的媒体播放进行介绍,首先梳理了整体的播放流程,然后对播放的主要步骤进行了详细地分析。 媒体播放主要分为以下几个层次:

(1)提供给应用调用的Native接口,这个实际上通过OHOS::CreatePlayer()调用返回PlayerImpl实例。

(2)PlayerClient,这部分通过IPC的proxy调用,向远程服务发起调用请求。

(3)PlayerServer,这部分是播放服务的实现端,提供给Client端调用。

(4)Gstreamer,这部分是提供给PlayerServer调用,真正实现媒体播放的功能。

更多热点文章阅读

  • 玩嗨OpenHarmony:基于OpenHarmony的智能助老服务机器人
  • 玩嗨OpenHarmony:基于OpenHarmony的智慧农业环境监控系统
  • 基于OpenHarmony的智慧牧场方案:生物姿态检测
  • 基于OpenHarmony的智慧牧场方案:生物心率检测篇
  • 使用ADS1115扩展ROC-RK3568-PC开发板ADC功能

提示:本文由电子发烧友社区发布,转载请注明以上来源。如需社区合作及入群交流,请添加微信EEFans0806,或者发邮箱liuyong@huaqiu.com。


原文标题:OpenHarmony 3.2 Beta多媒体系列:音视频播放框架

文章出处:【微信公众号:电子发烧友开源社区】欢迎添加关注!文章转载请注明出处。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 电子发烧友
    +关注

    关注

    33

    文章

    551

    浏览量

    32971
  • 开源社区
    +关注

    关注

    0

    文章

    94

    浏览量

    410

原文标题:OpenHarmony 3.2 Beta多媒体系列:音视频播放框架

文章出处:【微信号:HarmonyOS_Community,微信公众号:电子发烧友开源社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何在音频播放时插播音频

    ZDP14x0系列芯片是内置开源GUI引擎的图像显示专用驱动芯片,内部集成16MB/64MB显示内存、2D图形加速器、音视频解码器等丰富多媒体功能。不仅支持音视频
    的头像 发表于 11-25 15:40 605次阅读
    如何在音频<b class='flag-5'>播放</b>时插播音频

    dm368录制音视频后用vlc播放不同步是怎么回事?

    目前我们用其他的开发板 能够录制音视频,但是用vlc播放的时候发现每次都是视频播放完成了音频还要播放一会,随着录制时间加上,延后的这个时间
    发表于 10-15 06:56

    盘点那些常见音视频接口

    我们熟知的一些常见音视频接口,发展至今在日常使用中已经渐渐少了。但是在工业领域的音视频连接,依然能看到其身影。这些看似消失的接口,它们现在发展成什么样子了?本期我们将做一个大盘点。
    的头像 发表于 09-09 14:34 532次阅读

    常见音视频接口的静电浪涌防护和滤波方案

    音视频接口在现代多媒体设备中扮演着至关重要的角色,它们确保了音视频信号在不同设备间的顺畅传输,各种类型的音视频接口满足了多样化的应用场景需求。 在
    的头像 发表于 06-25 11:28 663次阅读

    分享一款高清HDMI音视频采集编码卡,支持双码流

    “灵卡CAS”系列HDMI音视频采集卡。独具匠心的设计与功能配置,适用于各种规模的商务会议、多媒体教育以及数字化展示等各类场景。
    的头像 发表于 05-07 11:43 402次阅读
    分享一款高清HDMI<b class='flag-5'>音视频</b>采集编码卡,支持双码流

    【RTC程序设计:实时音视频权威指南】音视频的编解码压缩技术

    音视频所载有的信息在通过传输的时候就需要压缩编码。 其中,文本压缩是指通过使用各种算法和技术,将文本数据表示为更紧凑的形式,以减少存储空间。 霍夫曼编码是一种无损压缩算法,它可以根据字符出现
    发表于 04-28 21:04

    鸿蒙媒体开发【简述】

    媒体系统架构 媒体系统提供用户视觉、听觉信息的处理能力,如音视频信息的采集、压缩存储、解压播放等。在操作系统实现中,通常基于不同的媒体信息处
    发表于 02-28 17:53

    OpenHarmony4.0源码解析之媒体框架

    媒体框架简介 媒体框架 multimedia_player_framework 主要提供音视频的录制与
    的头像 发表于 02-26 22:05 932次阅读
    <b class='flag-5'>OpenHarmony</b>4.0源码解析之<b class='flag-5'>媒体</b><b class='flag-5'>框架</b>

    音视频解码生成:打造你的专属高清影院体验

    在数字化时代,人们对观影体验的要求越来越高。音视频解码生成技术,作为现代多媒体播放的核心,正是为了满足这种需求而不断发展和完善的。通过这项技术,我们可以轻松打造属于自己的高清影院体验。 一、高清画质
    的头像 发表于 02-25 14:47 404次阅读

    音视频解码生成:打造极致观影体验的关键技术

    在现代多媒体时代,音视频解码生成技术已成为提供极致观影体验的核心要素。它不仅能够确保音视频数据的高效传输,还能保证播放的流畅性和画质清晰度,为用户带来身临其境的观影享受。 1. 解码生
    的头像 发表于 02-25 14:43 472次阅读

    音视频解码器优化技巧:提升播放体验的关键步骤

    随着数字多媒体内容的爆炸式增长,音视频解码器在现代技术生活中扮演着至关重要的角色。从流畅的在线视频播放到高质量的本地文件解码,解码器的性能直接影响了我们的观看体验。那么,如何优化
    的头像 发表于 02-21 14:45 808次阅读

    音视频解码器硬件加速:实现更流畅的播放效果

    随着多媒体内容的日益丰富和高清化,传统的软件解码已经难以满足人们对流畅播放体验的需求。因此,音视频解码器硬件加速技术的出现,为提升播放效果带来了革命性的改变。 硬件加速的原理 硬件加速
    的头像 发表于 02-21 14:40 964次阅读
    <b class='flag-5'>音视频</b>解码器硬件加速:实现更流畅的<b class='flag-5'>播放</b>效果

    音视频解码生成在多媒体制作中的应用

    音视频解码生成是多媒体制作中不可或缺的一部分,它扮演着将编码的音视频数据转化为可播放、可编辑的内容的关键角色。在多媒体制作的全过程中,
    的头像 发表于 02-21 14:39 379次阅读

    音视频解码生成与流媒体传输的结合

    音视频解码生成与流媒体传输是现代数字媒体技术中两个不可或缺的部分,它们的结合为用户提供了高质量、实时性的多媒体体验。 1. 解码生成与流媒体
    的头像 发表于 02-21 14:36 397次阅读

    万兴科技发布国内首个音视频多媒体大模型“天幕”

    万兴科技近日正式发布了国内首个音视频多媒体大模型——万兴“天幕”,并宣布大模型研发中心将正式落户马栏山。
    的头像 发表于 02-04 11:42 1306次阅读