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

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

3天内不再提示

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

电子发烧友开源社区 来源:未知 2022-11-25 09:10 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

1f5e2716-6c5d-11ed-8abf-dac502259ad0.png

巴延兴

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

资深OS框架开发工程师

一、 简介

多媒体播放框架主要的实现在PlayerServer服务中,这个服务提供了媒体播放框架所需要的实现环境,继续跟踪代码分析发现,PlayerServer主要通过gstreamer适配层,对gstreamer进行调用。gstreamer属于更加具体的实现,所以本篇文章主要是分析PlayerServer通过适配层调用到gstreamer的过程。

此前,我在《OpenHarmony 3.2 Beta多媒体系列-音视频播放框架》一文中,主要分析了多媒体播放的框架层代码,本地接口通过服务端的proxy代理类进行IPC调用,最终调用到PlayerServer服务端。本篇主要分析了多媒体gstreamer的调用,涉及到从PlayerServer到gstreamer的整体流程。

二、 目录

  gstreamer
    ├── BUILD.gn
    ├── common
    │   ├── BUILD.gn
    │   ├── playbin_adapter
    │   │   ├── i_playbin_ctrler.h
    │   │   ├── playbin2_ctrler.cpp
    │   │   ├── playbin2_ctrler.h
    │   │   ├── playbin_ctrler_base.cpp
    │   │   ├── playbin_ctrler_base.h
    │   │   ├── playbin_msg_define.h
    │   │   ├── playbin_sink_provider.h
    │   │   ├── playbin_state.cpp
    │   │   ├── playbin_state.h
    │   │   ├── playbin_task_mgr.cpp
    │   │   └── playbin_task_mgr.h
    │   ├── state_machine
    │   │   ├── state_machine.cpp
    │   │   └── state_machine.h
    ├── factory
    │   ├── BUILD.gn
    │   └── engine_factory.cpp
    └── player
        ├── BUILD.gn
        ├── player_codec_ctrl.cpp
        ├── player_codec_ctrl.h
        ├── player_engine_gst_impl.cpp
        ├── player_engine_gst_impl.h
        ├── player_sinkprovider.cpp
        ├── player_sinkprovider.h
        ├── player_track_parse.cpp
        └── player_track_parse.h

目录主要是多媒体子系统中的engine部分,涉及到了gstreamer的适配层,gstreamer具体的实现是在third_party/gstreamer目录中。

三 、Gstreamer介绍


1. 简介
Gstreamer是一个跨平台的多媒体框架,应用程序可以通过管道(Pipeline)的方式,将多媒体处理的各个步骤串联起来,达到预期的效果。每个步骤通过元素(Element)基于GObject对象系统通过插件(plugins)的方式实现,方便了各项功能的扩展。

1f86b4ce-6c5d-11ed-8abf-dac502259ad0.png

2.Gstreamer几个重要的概念
Element

Element是Gstreamer中最重要的对象类型之一。一个element实现一个功能(读取文件,解码,输出等),程序需要创建多个element,并按顺序将其串联起来,构成一个完整的Pipeline。

Pad

Pad是一个element的输入/输出接口,分为src pad(生产数据)和sink pad(消费数据)两种。两个element必须通过pad才能连接起来,pad拥有当前element能处理数据类型的能力(capabilities),会在连接时通过比较src pad和sink pad中所支持的能力,来选择最恰当的数据类型用于传输,如果element不支持,程序会直接退出。在element通过pad连接成功后,数据会从上一个element的src pad传到下一个element的sink pad然后进行处理。

Bin和Pipeline

Bin是一个容器,用于管理多个element,改变bin的状态时,bin会自动去修改所包含的element的状态,也会转发所收到的消息。如果没有bin,我们需要依次操作我们所使用的element。通过bin降低了应用的复杂度。

Pipeline继承自bin,为程序提供一个bus用于传输消息,并且对所有子element进行同步。当将Pipeline的状态设置为PLAYING时,Pipeline会在一个/多个新的线程中通过element处理数据。

四、调用流程

1fadedbe-6c5d-11ed-8abf-dac502259ad0.jpg1fd356d0-6c5d-11ed-8abf-dac502259ad0.jpg  

左右滑动查看更多

五、源码分析

1. PrepareAsync分析


首先,在PlayerServer的PrepareAsync中会调用OnPrepare(false),具体是在OnPrepare(false)中实现,参数传入false,表明调用的是异步方法。

int32_tPlayerServer::PrepareAsync()
{
    std::lock_guard<std::mutex> lock(mutex_);
    MEDIA_LOGW("KPI-TRACE: PlayerServer PrepareAsync in");


    if (lastOpStatus_ == PLAYER_INITIALIZED || lastOpStatus_ == PLAYER_STOPPED) {
        return OnPrepare(false);
    } else {
        MEDIA_LOGE("Can not Prepare, currentState is %{public}s", GetStatusDescription(lastOpStatus_).c_str());
        return MSERR_INVALID_OPERATION;
    }
}


OnPrepare方法中,先通过playerEngine_调用SerVideoSurface的方法,将surface_设置到PlayerEngineGstImpl中(producerSurface_),接着启动一个任务,调用目前状态的Prepare()方法。

int32_tPlayerServer::OnPrepare(boolsync)
{
    CHECK_AND_RETURN_RET_LOG(playerEngine_ != nullptr, MSERR_NO_MEMORY, "playerEngine_ is nullptr");
    int32_t ret = MSERR_OK;


#ifdef SUPPORT_VIDEO
    if (surface_ != nullptr) {
        ret = playerEngine_->SetVideoSurface(surface_);
        CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Engine SetVideoSurface Failed!");
    }
#endif


    lastOpStatus_ = PLAYER_PREPARED;


    auto preparedTask = std::make_sharedint32_t<>>([this]() {
        MediaTrace::TraceBegin("PlayerServer::PrepareAsync", FAKE_POINTER(this));
        auto currState = std::static_pointer_cast(GetCurrState());
        return currState->Prepare();
    });


    ret = taskMgr_.LaunchTask(preparedTask, PlayerServerTaskType::STATE_CHANGE);
    CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, ret, "Prepare launch task failed");


    if (sync) {
        (void)preparedTask->GetResult(); // wait HandlePrpare
    }
    return MSERR_OK;
}

进入Preparing状态后,会触发PlayerServer的HandlePrepare()方法被调用,在这个方法里会通过playerEngine_调用PrepareAsync方法,这个方法调用的是PlayerEngineGstImpl对应的PrepareAsync方法。

int32_tPlayerServer::HandlePrepare()
{
    int32_t ret = playerEngine_->PrepareAsync();
    CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Server Prepare Failed!");
    if (config_.leftVolume <= 1.0f || config_.rightVolume <= 1.0f) {
        ret = playerEngine_->SetVolume(config_.leftVolume, config_.rightVolume);
        MEDIA_LOGD("Prepared SetVolume leftVolume:%{public}f rightVolume:%{public}f, ret:%{public}d", 
                   config_.leftVolume, config_.rightVolume, ret);
    }
    (void)playerEngine_->SetLooping(config_.looping);


    {
        auto rateTask = std::make_sharedvoid<>>([this]() {
            auto currState = std::static_pointer_cast(GetCurrState());
            (void)currState->SetPlaybackSpeed(config_.speedMode);
        });


        (void)taskMgr_.LaunchTask(rateTask, PlayerServerTaskType::RATE_CHANGE);
    }
    return MSERR_OK;
}

首先初始化playBinCtrler_,后续的操作都是通过PlayBinCtrlerBase对象来操作的,所以PlayBinCtrlerInit()方法会创建PlayBinCtrlerBase对象(playBinCtrler_),创建好以后通过playBinCtrler_进行SetSource和SetXXXListener的设置。

int32_tPlayerEngineGstImpl::PrepareAsync()
{
    std::unique_lock<std::mutex> lock(mutex_);
    MEDIA_LOGD("Prepare in");


    int32_t ret = PlayBinCtrlerInit();
    CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_VAL, "PlayBinCtrlerInit failed");


    CHECK_AND_RETURN_RET_LOG(playBinCtrler_ != nullptr, MSERR_INVALID_VAL, "playBinCtrler_ is nullptr");
    ret = playBinCtrler_->PrepareAsync();
    CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, ret, "PrepareAsync failed");


    // The duration of some resources without header information cannot be obtained.
    MEDIA_LOGD("Prepared ok out");
    return MSERR_OK;
}

初始化完成以后,接下来进行playBinCtrler_的PrepareAsync的调用,PlayBinCtrlerBase中的PrepareAsync的方法间接地调用了PrepareAsyncInternal。

int32_tPlayBinCtrlerBase::PrepareAsync()
{
    MEDIA_LOGD("enter");


    std::unique_lock<std::mutex> lock(mutex_);
    return PrepareAsyncInternal();
}

PrepareAsyncInternal首先判断当前的状态,如果是preparingState或preparedState,那么就直接返回成功,否则继续向下调用。接下来会调用EnterInitializedState(),这个方法中会创建playbin,设置signal的回调以及gstreamer参数的设置。最后调用目前状态的Prepare方法,此时的状态是InitializedState。

int32_t PlayBinCtrlerBase::PrepareAsyncInternal()
{
    if ((GetCurrState() == preparingState_) || (GetCurrState() == preparedState_)) {
        MEDIA_LOGI("already at preparing state, skip");
        return MSERR_OK;
    }


    CHECK_AND_RETURN_RET_LOG((!uri_.empty() || appsrcWrap_), MSERR_INVALID_OPERATION, "Set uri firsty!");


    int32_t ret = EnterInitializedState();
    CHECK_AND_RETURN_RET(ret == MSERR_OK, ret);


    auto currState = std::static_pointer_cast(GetCurrState());
    ret = currState->Prepare();
    CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, ret, "PrepareAsyncInternal failed");


    return MSERR_OK;
}

InitializedState的Prepare方法又通过ctrler_调回到PlayBinCtrlerBase的ChangeState方法,这个方法是在PlayBinCtrlerBase的父类StateMachine中,它是一个状态机,管理着各种状态的切换。

int32_t PlayBinCtrlerBase::Prepare()
{
    ctrler_.ChangeState(ctrler_.preparingState_);
    return MSERR_OK;
}

很多表示状态的类在PlayBinCtrlerBase中进行声明,这些子类的具体实现功能在playbin_state.cpp中。

private:
    class BaseState;
    class IdleState;
    class InitializedState;
    class PreparingState;
    class PreparedState;
    class PlayingState;
    class PausedState;
    class StoppedState;
    class StoppingState;
    class PlaybackCompletedState;

接下来看一下状态机的ChangeState方法,可以看出切换状态的时候,先调用切换前状态的StateExit()方法,再调用切换后状态的StateEnter()。如果需要一些操作,我们可以在状态的StateEnter和StateExit中进行。

voidStateMachine::ChangeState(conststd::shared_ptr&state)
{
    ......
    if (currState_ != nullptr && currState_->GetStateName() == "stopping_state" && state->GetStateName() != "stopped_state") {
        return;
    }
    if (currState_) {
        currState_->StateExit();
    }
    currState_ = state;
    state->StateEnter();
}

因为上面切换状态调用的是ctrler_.ChangeState(ctrler_.preparingState_),所以接下来看一下PreparingState状态的StateEnter方法。这个方法中首先是调用了ctrler_.ReportMessage(msg),字面上看是用来上报msg信息的。

voidPlayBinCtrlerBase::StateEnter()
{
    PlayBinMessage msg = { PLAYBIN_MSG_SUBTYPE, PLAYBIN_SUB_MSG_BUFFERING_START, 0, {} };
    ctrler_.ReportMessage(msg);


    GstStateChangeReturn ret;
    (void)ChangePlayBinState(GST_STATE_PAUSED, ret);


    MEDIA_LOGD("PreparingState::StateEnter finished");
}

ctrler_是PlayBinCtrlerBase类型的变量,直接看PlayBinCtrlerBase的ReportMessage方法,这个方法的核心,是创建一个任务后,将任务放入消息队列中,等待消息被处理,这里我们最想知道的是这个消息会在什么地方被处理。msgReportHandler创建了TaskHandler,这个里面会调用notifier_(msg),这里的notifier_比较重要,我们可以顺着这个变量向上分析。

voidPlayBinCtrlerBase::ReportMessage(constPlayBinMessage&msg)
{
    ......
    auto msgReportHandler = std::make_sharedvoid<>>([this, msg]() { notifier_(msg); });
    int32_t ret = msgQueue_->EnqueueTask(msgReportHandler);
    if (ret != MSERR_OK) {
        MEDIA_LOGE("async report msg failed, type: %{public}d, subType: %{public}d, code: %{public}d",
                   msg.type, msg.subType, msg.code);
    };


    if (msg.type == PlayBinMsgType::PLAYBIN_MSG_EOS) {
        ProcessEndOfStream();
    }
}

notifier_是在PlayBinCtrlerBase被创建的时候赋值的。

PlayBinCtrlerBase::PlayBinCtrlerBase(constPlayBinCreateParam&createParam)
    : renderMode_(createParam.renderMode),
    notifier_(createParam.notifier),
    sinkProvider_(createParam.sinkProvider)
{
    MEDIA_LOGD("enter ctor, instance: 0x%{public}06" PRIXPTR "", FAKE_POINTER(this));
}

在源码分析的前期PlayerEngineGstImpl初始化PlayBinCtrlerBase的时候进行了创建notifier = std::bind(&PlayerEngineGstImpl::OnNotifyMessage, this, std::_1) notifier相当于是调用了PlayerEngineGstImpl::OnNotifyMessage方法。所以上述中的处理函数就是PlayerEngineGstImpl::OnNotifyMessage。

int32_tPlayerEngineGstImpl::PlayBinCtrlerPrepare()
{
    uint8_t renderMode = IPlayBinCtrler::DEFAULT_RENDER;
    auto notifier = std::bind(&PlayerEngineGstImpl::OnNotifyMessage, this, std::_1);


    {
        std::unique_lock<std::mutex> lk(trackParseMutex_);
        sinkProvider_ = std::make_shared(producerSurface_);
        sinkProvider_->SetAppInfo(appuid_, apppid_);
    }


    IPlayBinCtrler::PlayBinCreateParam createParam = {
        static_cast(renderMode), notifier, sinkProvider_
    };
    playBinCtrler_ = IPlayBinCtrler::PLAYBIN2, createParam);
    ......
    return MSERR_OK;
}

在OnNotifyMessage中指定了各种消息类型对应的执行函数,上述代码中创建的Message类型是PLAYBIN_MSG_SUBTYPE,子类型为PLAYBIN_SUB_MSG_BUFFERING_START。

voidPlayerEngineGstImpl::OnNotifyMessage(constPlayBinMessage&msg)
{
    const std::unordered_map MSG_NOTIFY_FUNC_TABLE = {,>
        { PLAYBIN_MSG_ERROR, std::bind(&PlayerEngineGstImpl::HandleErrorMessage, this, std::_1) },
        { PLAYBIN_MSG_SEEKDONE, std::bind(&PlayerEngineGstImpl::HandleSeekDoneMessage, this, std::_1) },
        { PLAYBIN_MSG_SPEEDDONE, std::bind(&PlayerEngineGstImpl::HandleInfoMessage, this, std::_1) },
        { PLAYBIN_MSG_BITRATEDONE, std::bind(&PlayerEngineGstImpl::HandleInfoMessage, this, std::_1)},
        { PLAYBIN_MSG_EOS, std::bind(&PlayerEngineGstImpl::HandleInfoMessage, this, std::_1) },
        { PLAYBIN_MSG_STATE_CHANGE, std::bind(&PlayerEngineGstImpl::HandleInfoMessage, this, std::_1) },
        { PLAYBIN_MSG_SUBTYPE, std::bind(&PlayerEngineGstImpl::HandleSubTypeMessage, this, std::_1) },
        { PLAYBIN_MSG_AUDIO_SINK, std::bind(&PlayerEngineGstImpl::HandleAudioMessage, this, std::_1) },
        { PLAYBIN_MSG_POSITION_UPDATE, std::bind(&PlayerEngineGstImpl::HandlePositionUpdateMessage, this,
            std::_1) },
    };
    if (MSG_NOTIFY_FUNC_TABLE.count(msg.type) != 0) {
        MSG_NOTIFY_FUNC_TABLE.at(msg.type)(msg);
    }
}

最终的流程走到了PlayerEngineGstImpl::HandleBufferingStart(),在这个方法中,主要通过obs_将format传给IPlayerEngineObs的OnInfo方法。

voidPlayerEngineGstImpl::HandleBufferingStart()
{
    percent_ = 0;
    Format format;
(void)format.PutIntValue(std::string(PlayerKeys::PLAYER_BUFFERING_START), 0);
    std::shared_ptr notifyObs = obs_.lock();
    if (notifyObs != nullptr) {
        notifyObs->OnInfo(INFO_TYPE_BUFFERING_UPDATE, 0, format);
    }
}

我们重点看一下obs_是哪里设置的,在PlayerServer的初始化InitPlayEngine。shared_from_this()相当于是把PlayerServer自身赋值给obs,PlayerServer也是实现了IPlayerEngineObs对应的接口。

int32_tPlayerServer::InitPlayEngine(conststd::string&url)
{
    ......
    int32_t ret = taskMgr_.Init();
    auto engineFactory = EngineFactoryRepo::SCENE_PLAYBACK, url);
    
    playerEngine_ = engineFactory->CreatePlayerEngine(appUid_, appPid_);


    if (dataSrc_ == nullptr) {
        ret = playerEngine_->SetSource(url);
    } else {
        ret = playerEngine_->SetSource(dataSrc_);
    }


    std::shared_ptr obs = shared_from_this();
    ret = playerEngine_->SetObs(obs);


    lastOpStatus_ = PLAYER_INITIALIZED;
    ChangeState(initializedState_);


    return MSERR_OK;
}

这样我们就跟踪到了PlayerServer的OnInfo()方法。

voidPlayerServer::OnInfo(PlayerOnInfoTypetype,int32_textra,constFormat&infoBody)
{
    std::lock_guard<std::mutex> lockCb(mutexCb_);


    int32_t ret = HandleMessage(type, extra, infoBody);
    if (playerCb_ != nullptr && ret == MSERR_OK) {
        playerCb_->OnInfo(type, extra, infoBody);
    }
}

2. Play分析

从PlayerServer开始跟踪,调用到PlayerServer的OnPlay()方法。

int32_tPlayerServer::Play()
{
    ......
    if (lastOpStatus_ == PLAYER_PREPARED || lastOpStatus_ == PLAYER_PLAYBACK_COMPLETE ||
        lastOpStatus_ == PLAYER_PAUSED) {
        return OnPlay();
    } else {
        return MSERR_INVALID_OPERATION;
    }
}

在OnPlay中会启动一个任务,在任务中获取当前的状态,然后调用当前状态的Play()方法。

int32_tPlayerServer::OnPlay()
{
    ......
    auto playingTask = std::make_sharedvoid<>>([this]() {
        auto currState = std::static_pointer_cast(GetCurrState());
        (void)currState->Play();
    });


    int ret = taskMgr_.LaunchTask(playingTask, PlayerServerTaskType::STATE_CHANGE);


    lastOpStatus_ = PLAYER_STARTED;
    return MSERR_OK;
}

前面调用了PrepareAsync,所以当前的状态是Prepared,调用到了PreparedState的Play()方法,这个方法还是按照之前Prepare的方式,调回到PlayerServer的HandlePlay()。

int32_tPlayerServer::Play()
{
    return server_.HandlePlay();
}

在PlayServer中通过播放引擎继续向下调用。

int32_tPlayerServer::HandlePlay()
{
    int32_t ret = playerEngine_->Play();
    CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, MSERR_INVALID_OPERATION, "Engine Play Failed!");


    return MSERR_OK;
}

在PlayerEngineGstImpl的Play()方法会继续调用playBinCtrler_的Play()方法。

int32_tPlayerEngineGstImpl::Play()
{
    ......
    playBinCtrler_->Play();
    return MSERR_OK;
}

PlayBinCtrlerBase的Play()方法根据当前的State,调用currSate->Play()。

int32_tPlayBinCtrlerBase::Play()
{
    ......
    auto currState =    std::static_pointer_cast(GetCurrState());
    int32_t ret = currState->Play();


    return MSERR_OK;
}

在PreparedState的Play()方法中改变了PlayBin的状态为playing。

int32_tPlayBinCtrlerBase::Play()
{
    GstStateChangeReturn ret;
    return ChangePlayBinState(GST_STATE_PLAYING, ret);
}

ChangePlayBinState主要是调用了gst_element_set_state(GST_ELEMENT_CAST(ctrler_.playbin_),GST_STATE_PLAYING),这个直接调用了gstreamer三方库的实现,调用完这个方法以后,gstreamer就开始进行播放了。

int32_tPlayBinCtrlerBase::ChangePlayBinState(GstStatetargetState,GstStateChangeReturn&ret)
{
    ......
    ret = gst_element_set_state(GST_ELEMENT_CAST(ctrler_.playbin_), targetState);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        MEDIA_LOGE("Failed to change playbin's state to %{public}s", gst_element_state_get_name(targetState));
        return MSERR_INVALID_OPERATION;
    }


    return MSERR_OK;
}

六、总结

本篇文章主要从PlayerServer播放服务开始分析音视频播放的流程,涉及到gstreamer引擎的调用,相对于多媒体播放框架来说,更加底层,便于熟悉从框架到gstreamer的整体流程。

更多热点文章阅读

  • 玩嗨OpenHarmony:基于OpenHarmony的智能助老服务机器人
  • 玩嗨OpenHarmony:基于OpenHarmony的智慧农业环境监控系统
  • 首个通过OpenHarmony兼容性测评的全场景实验箱
  • 基于OpenHarmony的智能门禁系统,让出行更便捷
  • OpenHarmony 3.2 Beta多媒体系列:音视频播放框架

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


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

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


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

    关注

    33

    文章

    584

    浏览量

    33982
  • 开源社区
    +关注

    关注

    0

    文章

    95

    浏览量

    776

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

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

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    音视频编解码封装解封装部件介绍

    是否有探索开源鸿蒙音视频编解码技术的欲望?是否对开源鸿蒙音视频编解码格式支持有诉求?别急——今天这份开源鸿蒙AVCodec Kit介绍文章,就是解答疑惑的指南!参考这份指南,可以使用开源鸿蒙的音视频编解码能力,进行应用开发;也可
    的头像 发表于 10-31 09:15 257次阅读
    <b class='flag-5'>音视频</b>编解码封装解封装部件介绍

    音视频开发全栈解析

    Media Kit 让开发者可以调用系统的音视频能力,涵盖播放、录制、录屏、元数据提取与转码六大核心功能模块,支撑系统应用与第三方应用在视频播放、内容创作、教学录屏、直播互动等典型场景
    的头像 发表于 09-18 14:42 586次阅读
    <b class='flag-5'>音视频</b>开发全栈解析

    一款面向便携式多媒体应用的高品质立体声数模转换器 (DAC) 芯片

    便携式多媒体播放器(Portable Media Player),简称PMP,是集音视频播放、图片浏览与数据存储功能于一体的手持设备。
    的头像 发表于 09-04 10:00 649次阅读
    一款面向便携式<b class='flag-5'>多媒体</b>应用的高品质立体声数模转换器 (DAC) 芯片

    千视电子受邀亮相2025音视频产业链研讨会,引领全链路音视频IP化新趋势

    2025年8月22日,由中国电子音响行业协会和湖南省工业和信息化厅联合主办的“2025音视频产业链发展研讨暨对接会”在长沙举行。活动汇聚音视频产业链上下游企业,以及长沙市相关政府领导与行业
    的头像 发表于 08-27 11:33 1028次阅读
    千视电子受邀亮相2025<b class='flag-5'>音视频</b>产业链研讨会,引领全链路<b class='flag-5'>音视频</b>IP化新趋势

    泰芯半导体推出星闪音视频无线SOC芯片TXW828

    在短距离无线通信技术加速迭代的浪潮中,珠海泰芯半导体有限公司全球首先发布支持星闪(NearLink)标准的音视频无线SOC芯片——TXW828。这款集WiFi/蓝牙BLE/星闪三模融合音视频无线芯片
    的头像 发表于 06-20 15:51 2280次阅读

    乐鑫ESP-RTC实时音视频通信方案

    ESP-RTC乐鑫科技推出ESP-RTC(Real-TimeCommunication)音视频通信方案,能够实现稳定流畅、超低延时的语音和视频实时通信。ESP-RTC以乐鑫
    的头像 发表于 05-26 18:07 1256次阅读
    乐鑫ESP-RTC实时<b class='flag-5'>音视频</b>通信方案

    达辰威执法音视频管理平台全新升级

    在传统执法场景中,海量音视频数据的存储、分析和监管一直是难题——无效视频堆积如山、关键信息难以快速提取、执法规范性缺乏实时监督……在科技赋能执法的时代背景下,达辰威科技集团推出全新升级的执法音视频
    的头像 发表于 05-20 15:06 785次阅读

    中国音视频产业迈向高质量发展

    随着超高清先锋计划的逐步推进,截至2024年底,中国音视频产业规模超5万亿,包括个人消费电子、家庭影音娱乐、行业多媒体应用及车载视听产业和相关衍生应用等行业企业超过百万家,音视频产业已经成为国家数字经济发展的重要支柱。
    的头像 发表于 04-24 11:10 1077次阅读

    音视频一体化解决方案

    。在应急指挥中心、多媒体会议室、移动指挥车、多媒体教室、数字法庭等场景中,用户亟需一套高度集成、智能管控、互联互通的一体化解决方案,以实现音视频信号的综合调度与高效协作。
    的头像 发表于 04-24 09:14 900次阅读

    实用调试技能:全志T113-i 音视频测试

    前言:音视频功能是现代嵌入式系统中的核心应用之一,尤其在全志T113-i开发板中,其丰富的音视频接口为开发者提供了强大的开发能力。本篇文章将带你快速掌握T113-i平台下音视频模块的调试技能,通过
    的头像 发表于 03-06 08:31 2274次阅读
    实用调试技能:全志T113-i <b class='flag-5'>音视频</b>测试

    【北京迅为】itop-3568 开发板openharmony鸿蒙烧写及测试-第2章OpenHarmony v3.2-Beta4版本测试

    【北京迅为】itop-3568 开发板openharmony鸿蒙烧写及测试-第2章OpenHarmony v3.2-Beta4版本测试
    的头像 发表于 03-05 10:53 888次阅读
    【北京迅为】itop-3568 开发板<b class='flag-5'>openharmony</b>鸿蒙烧写及测试-第2章<b class='flag-5'>OpenHarmony</b> v<b class='flag-5'>3.2-Beta</b>4版本测试

    RCA接口音视频传输的原理

    RCA接口音视频传输的原理主要基于模拟信号的传输方式。以下是对其传输原理的介绍: 一、RCA接口的基本结构 RCA接口,又称AV接口或莲花插座,其结构包括一个圆形的插头和一个相应的插座。插头上通常
    的头像 发表于 02-17 15:36 2306次阅读

    RK628H:高端音视频处理与传输芯片详解

    RK628H是一款集高清音视频处理与传输功能于一体的高端芯片,专为满足现代多媒体设备对高分辨率、高帧率视频以及高质量音频的需求而设计。其强大的音视频处理能力和多样化的输入输出接口,使得
    的头像 发表于 02-10 17:56 2042次阅读

    国科微荣获音视频领域关键技术突破一等奖

    近日,第五届“马栏山杯”国际音视频算法大赛-2024音视频领域关键技术突破奖揭晓,国科微8K超高清视频系列芯片凭借优异的产品性能及示范性应用获评一等奖。
    的头像 发表于 12-26 15:11 867次阅读

    RK3588核心板多媒体功能一览

    前言:RK3588支持8K视频编码和解码,以及多路视频源同时解码,为视频处理和多媒体应用提供了强大的支持,本文则从音视频两个功能测试上来认识
    的头像 发表于 12-12 08:31 3183次阅读
    RK3588核心板<b class='flag-5'>多媒体</b>功能一览