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

OpenHarmony 3.2 Beta多媒体系列——音视频播放gstreamer 多媒体播放框架主要的实现在PlayerServer服务中,这个服务提供了媒体播放框架所需要的实现环境,继续跟踪代码分析发现,PlayerServer主要通过gstreamer适配层,对gstreamer进行调用。gstreamer属于更加具体的实现,所以本篇文章主要是分析PlayerServer通过适配层调用到gstreamer的过程。本篇主要分析了多媒体gstreamer的调用,涉及到从PlayerServer到gstreamer的整体流程。1. 简介。 阅读详情

目录

一、简介

二、目录

三、播放的总体流程

四、Native接口使用

五、调用流程

六、总结


一、简介

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

二、目录

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服务端

三、播放的总体流程

四、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::Media::PlayerFactory::CreatePlayer();
    if (player_ == nullptr) {
        cout << "player_ is null" << endl;
        return;
    }
    RegisterTable();
    std::shared_ptr<PlayerCallbackDemo> cb = std::make_shared<PlayerCallbackDemo>();
    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<Surface> 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::getline(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::string::npos && dataSrc_ != nullptr) {
                dataSrc_->Reset();
            }
            continue;
        } else if (cmd.find("quit") != std::string::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方法,默认不输入命令参数时,是播放操作。

五、调用流程

本段落主要针对媒体播放的框架层代码进行分析,所以在流程中涉及到了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<IPlayerService> MediaClient::CreatePlayerService()
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (!IsAlived()) {
        MEDIA_LOGE("media service does not exist.");
        return nullptr;
    }

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

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

    std::shared_ptr<PlayerClient> 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<std::mutex> 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<IPlayerService> PlayerServer::Create()
{
    std::shared_ptr<PlayerServer> server = std::make_shared<PlayerServer>();
    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<std::mutex> 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<TaskHandler<void>>([this]() {
        MediaTrace::TraceBegin("PlayerServer::Play", FAKE_POINTER(this));
        auto currState = std::static_pointer_cast<BaseState>(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::PreparedState::Play()

int32_t PlayerServer::PreparedState::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::Media::PlayerFactory::CreatePlayer()调用返回PlayerImpl实例。

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

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

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

OpenHarmony 3.2 Beta源码分析之MediaLibrary OpenAtom OpenHarmony(以下简称“OpenHarmony”)MediaLibrary媒体库提供了一系列易用的接口用于获取媒体文件元数据信息。MediaLibrary接口是OpenHarmony系统内部使用接口,不是外部三方应用使用的接口。MediaLibrary的功能支持扫描设备中所有媒体文件的能力,为相册、音视频播放器、文件管理器等应用提供统一的媒体元数据信息操作接口,目前可实现的具体能力如下:● 查询音频、视频和图片文件元数据信息● 查询图片和视频相册。 阅读详情

相关推荐

【鸿蒙南向开发实战】OpenHarmony之媒体组件模块简介

本文中我们讨论了OpenHarmony 3.2 Release中媒体组件模块的功能以及结构,并且对其实现的原理进行了一些简单的探讨。GStreamer是一个功能强大的多媒体库,上文中作为例子讨论的PlayerEngine使用的是一款"全自动"的pipeline。而OpenHarmony中也有像AVCodecEngine那种OpenHarmony化的pipeline,包含了依赖于OpenHarmony平台编解码能力的插件。涉及的内容不单单包括媒体组件跟GStreamer,还包括驱动相关的内容。

HarmonyOS_666的博客 952

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

从框架图中可以看出,媒体框架的主要工作模式为通过 Gstreamer 的插件自动化注册及插件组合功能,将其余媒体播放相关的框架功能插件化,配合 Gstreamer 自身丰富的插件,共同来对外提供音视频的录制与播放功能。如通过 audio-sink 及 audio-source 插件调用音频框架的播放及采集功能来实现音频的播放与录制;通过 surface-sink 调用图形框架,video-decoder 调用解码驱动模块实现视频的硬解播放等。

maniuT的博客 1565

WSL2双显卡配置实战:避开Tesla与TCC模式的那些坑

本文针对WSL2环境下无法识别Tesla计算卡的问题进行深度解析。核心原因在于WSL2目前仅支持WDDM模式的显卡,而Tesla系列及运行在TCC模式下的显卡因采用纯计算架构而无法兼容。文章提供了详细的排查步骤,并给出了包括调整显卡用途、使用虚拟机或物理机在内的多种实用解决方案,帮助开发者避开配置陷阱。

weixin_29289821的博客 538

OpenHarmony多媒体播放器框架详解

首先根据RunCase可以大致了解一下播放音视频的主要流程,创建播放器,设置播放源,设置回调方法(包含播放过程中的多种状态的回调),设置播放显示的Surface,这些准备工作做好之后,需要调用播放器的PrepareASync方法,这个方法完成后,播放状态会变成Prepared状态,这时就可以调用播放器的play接口,进行音视频的播放了。RegisterTable()方法中,将字符串和对应的方法映射到Map中,这样后续的DoNext会根据输入的命令,来决定播放器具体的操作。这个方法中发起了一个播放的任务。

程序猿老樊的博客 1524

告别编译噩梦:Vcpkg+Cmake一站式集成Tesseract-OCR C++开发环境(Windows)

本文详细介绍了如何在Windows系统上使用Vcpkg和CMake快速集成Tesseract-OCR C++开发环境,解决传统编译过程中的依赖问题。通过一键安装和自动化配置,开发者可以高效搭建OCR识别系统,支持中英文识别,并提供了常见问题的解决方案和进阶技巧。

weixin_29009339的博客 231

C++笔试题整理

目录 1. 笔试题1 1.1 链表反转 1.2 String 2. 笔试题2 2.1 求下面函数的返回值(微软) 2.2 什么是“引用”?申明和使用“引用”要注意哪些问题? 2.3 将“引用”作为函数参数有哪些特点? 2.4 在什么时候需要使用“常引用”?  2.5 将“引用”作为函数返回值类型的格式、好处和需要遵守的规则? 2.6 “引用”与多态的关系? 2.7 “引用”与指针的区别是什么? 2.8 什么时候需要“引用”? 2.9 结构与联合有和区别? 2.10 下面关于.

星月相随的专栏 2483

鸿蒙5.0版开发:ArkUI框架开发-视频播放 (Video)

Video组件用于播放视频文件并控制其播放状态,常用于为短视频和应用内部视频的列表页面。

m0_73088370的博客 980

PT32F030与MT6826S SPI通讯实战:从硬件连接到数据交互全流程解析

本文详细解析了PT32F030与MT6826S通过SPI通讯的全流程,包括硬件连接、电路设计、SPI外设初始化配置以及数据交互协议实现。通过实战案例和代码示例,帮助开发者快速掌握PT32F030与MT6826S的SPI通讯技术,实现精准的角度和速度数据采集。

weixin_29189373的博客 420

OpenHarmony 3.1 Beta版本关键特性解析——HiStreamer轻量级可定制的媒体管线框架

(以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点) 陈国栋 数字多媒体技术在过去的数十年里得到了飞速的发展,多媒体终端设备如智能音箱、智能门锁、智能手表广泛应用于人们生活中。如此小的设备却能承载如此多功能是如何实现的呢?HiStreamer 轻量级可定制的媒体管线框架为你解答! 接下来,咱们一起探秘 HiStreamer 轻量级可定制的媒体管线框架。 一、媒体管线框架是什么? 在讲 HiStreamer 之前,咱们先来了解下媒体管线框架是什么?.

OpenHarmony_dev的博客 1548

【Bluetooth|蓝牙开发】二、蓝牙开发入门

蓝牙,是一种利用低功率无线电,支持设备短距离通信的无线电技术,能在包括移动电话、PDA、无线耳机、笔记本电脑、相关外设等众多设备之间进行无线信息交换,蓝牙工作在全球通用的2.4GHz ISM(即工业、科学、医学)频段,使用IEEE802.11协议。

Donge's Blog 1万+

鸿蒙开发实战:5步搞定FFmpeg移植(附OpenHarmony-v2.2-Beta配置)

本文详细解析了将FFmpeg移植到鸿蒙(OpenHarmony)生态的完整实战过程。针对OpenHarmony-v2.2-Beta的编译环境特点,文章提供了从环境准备、工具链配置、编译脚本编写到验证调试的五步实战指南,并深入探讨了移植过程中的常见问题与解决方案,帮助开发者高效完成多媒体框架的移植工作。

g8f9d0s1a2的博客 695

详解FreeRTOS:事件标志组(高级篇—5)

事件组可以实现一对多,多对多的同步,即一个任务可以等待多个事件的发生。任务可以通过设置事件位来实现事件的触发和等待操作,FreeRTOS的事件仅用于同步,不提供数据传输功能。

不脱发的程序猿 1073

OpenHarmony AVCodec模块分析(六):速看5.0-Beta1更新了什么

本文将介绍OpenHarmony-v5.0-beta1 AVCodec模块更新的内容及应用的调用流程。

m0_64778763的博客 1791

LLCC68芯片系统架构与LoRa驱动开发核心解析

LoRa射频收发器是低功耗广域物联网(LPWAN)的关键组件,其本质是集成了调制解调器、协议引擎、数据缓冲区与电源管理的专用SoC。理解其工作模式状态机(Sleep/Standby/TX/RX)与SPI主机接口时序规范,是实现稳定通信的基础。LLCC68通过寄存器映射与命令集双接口抽象硬件控制,支持LoRa扩频调制与FSK兼容模式,具备高链路预算(157 dB)和DC-DC/LDO动态电源切换能力。典型应用场景包括远距离传感器节点、智能表计与工业无线监测系统。本文深入剖析LLCC68的信号链路径、中断机制与

weixin_29859471的博客 281

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

鸿蒙(API 12 Beta3版)【使用MovingPhotoView播放动态照片】Media Library Kit媒体文件管理服务

- 当前不支持动态属性设置。 - 当前不支持设置ArkUI通用属性[expandSafeArea]。 - 该组件长按触发播放时组件区域放大为1.1倍。 - 该组件使用[AVPlayer]进行播放,同时开启的AVPlayer个数不建议超过3个,超过3个可能会出现视频播放卡顿现象。

鸿蒙的技术博客 1990

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Openharmony 媒体组件系统参数全梳理

OpenHarmony 媒体组件系统属性全梳理

m0_64778763的博客 1203

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

OpenHarmony媒体子系统详解

图1 OpenHarmony媒体子系统架构图。OpenHarmony媒体子系统为开发者提供一套简单且易于理解的接口,使得开发者能够方便接入系统并使用系统的媒体资源。2、OpenHarmony媒体子系统常用功能。5、OpenHarmony媒体子系统构成简介。├── frameworks # 框架代码。└── test # 测试代码。3、OpenHarmony媒体子系统架构。│ └── kits # 对外接。├── interfaces # 接口。1、OpenHarmony媒体子系统。

程序猿老樊的博客 1329

OpenHarmony视频编解码入坑记录

OHOS4.0适配riscv64架构的视频编解码入坑记录

qqq1112345的博客 2500

OpenHarmony之媒体组件模块简介

services目录下的内容可以粗略的理解为多媒体的系统服务,对应为OpenHarmony中的media_service进程,其下的engine目录中的内容是其核心所在,而services目录下主要是与frameworks/native下同名的接口,用于实现C/S模式。因为流水线上的每个环节都预先设置了其可以支持的数据类型与参数,在很多情况下,pipeline的结构也是根据数据的内容自动组建的(具体的协商过程不在本文讨论范围内)。从结构图中可以看出,提供给应用的接口层在基本能力的基础上提供了相当多的功能。

1 806
上一篇: OpenHarmony开发之MQTT讲解
下一篇: OpenHarmony 3.2 Beta多媒体系列——音视频播放gstreamer
OpenHarmony开发者
博客等级 码龄5年 626粉丝 221原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值