Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions media/server/gstplayer/include/GenericPlayerContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,21 @@ struct GenericPlayerContext
*/
std::unique_ptr<IGstProfiler> gstProfiler;

/**
* @brief True when first audio frame has already been scheduled for the current audio source lifecycle.
*/
bool firstAudioFrameReceived{false};

/**
* @brief Fallback probe id for first audio frame detection on sink pad.
*/
gulong audioFirstFrameProbeId{0};

/**
* @brief Fallback sink pad that owns audio first frame probe.
*/
GstPad *audioFirstFrameProbePad{nullptr};

/**
* @brief The audio position set in the GstSegment.
*/
Expand Down
6 changes: 5 additions & 1 deletion media/server/gstplayer/include/GstGenericPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva
void scheduleAudioUnderflow() override;
void scheduleVideoUnderflow() override;
void scheduleFirstVideoFrameReceived() override;
void scheduleFirstAudioFrameReceived(AudioFirstFrameAction audioAction) override;
void setAudioFirstFrameFallbackProbe(GstPad *pad, gulong id) override;
void clearAudioFirstFrameFallbackProbe() override;
void clearAudioFirstFrameFallbackProbeState() override;
void scheduleAllSourcesAttached() override;
bool setVideoSinkRectangle() override;
bool setImmediateOutput() override;
Expand Down Expand Up @@ -410,7 +414,7 @@ class GstGenericPlayer : public IGstGenericPlayer, public IGstGenericPlayerPriva
/**
* @brief Pushes GstSample if playback position has changed or new segment needs to be sent.
*
* @param[in] source : The Gst Source element, that should receive new sample
* @param[in] source : The Gst Source element, that should receive new sample
* @param[in] mediaSourceType : The media source type
*/
void pushSampleIfRequired(GstElement *source, const MediaSourceType &mediaSourceType);
Comment thread
Koky2701 marked this conversation as resolved.
Expand Down
32 changes: 32 additions & 0 deletions media/server/gstplayer/include/GstPlayerTypes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* If not stated otherwise in this file or this component's LICENSE file the
* following copyright and licenses apply:
*
* Copyright 2026 Sky UK
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef FIREBOLT_RIALTO_SERVER_GST_PLAYER_TYPES_H_
#define FIREBOLT_RIALTO_SERVER_GST_PLAYER_TYPES_H_

namespace firebolt::rialto::server
{
enum class AudioFirstFrameAction
{
CLEAR_PROBE,
CLEAR_PROBE_STATE
};
} // namespace firebolt::rialto::server

#endif // FIREBOLT_RIALTO_SERVER_GST_PLAYER_TYPES_H_
24 changes: 24 additions & 0 deletions media/server/gstplayer/include/IGstGenericPlayerPrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#ifndef FIREBOLT_RIALTO_SERVER_I_GST_GENERIC_PLAYER_PRIVATE_H_
#define FIREBOLT_RIALTO_SERVER_I_GST_GENERIC_PLAYER_PRIVATE_H_

#include "GstPlayerTypes.h"
#include "IMediaPipeline.h"

#include <gst/app/gstappsrc.h>
Expand Down Expand Up @@ -66,6 +67,29 @@ class IGstGenericPlayerPrivate
*/
virtual void scheduleFirstVideoFrameReceived() = 0;

/**
* @brief Schedules first audio frame received task. Called by the Gstreamer thread.
*/
virtual void scheduleFirstAudioFrameReceived(AudioFirstFrameAction audioAction) = 0;

/**
* @brief Stores audio first-frame fallback probe state.
*
* @param[in] pad : sink pad with installed probe
* @param[in] id : probe id
*/
virtual void setAudioFirstFrameFallbackProbe(GstPad *pad, gulong id) = 0;
Comment thread
skywojciechowskim marked this conversation as resolved.

/**
* @brief Removes and clears audio first-frame fallback probe state.
*/
virtual void clearAudioFirstFrameFallbackProbe() = 0;

/**
* @brief Clears audio first-frame fallback probe state without removing the probe.
*/
virtual void clearAudioFirstFrameFallbackProbeState() = 0;

/**
* @brief Schedules all sources attached task. Called by the worker thread.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#define FIREBOLT_RIALTO_SERVER_I_GENERIC_PLAYER_TASK_FACTORY_H_

#include "GenericPlayerContext.h"
#include "GstPlayerTypes.h"
#include "IDataReader.h"
#include "IFlushWatcher.h"
#include "IGstGenericPlayerPrivate.h"
Expand Down Expand Up @@ -398,9 +399,9 @@ class IGenericPlayerTaskFactory
*
* @retval the new FirstFrameReceived task instance.
*/
virtual std::unique_ptr<IPlayerTask> createFirstFrameReceived(GenericPlayerContext &context,
IGstGenericPlayerPrivate &player,
MediaSourceType sourceType) const = 0;
virtual std::unique_ptr<IPlayerTask>
createFirstFrameReceived(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, MediaSourceType sourceType,
AudioFirstFrameAction audioAction = AudioFirstFrameAction::CLEAR_PROBE) const = 0;

/**
* @brief Creates an UpdatePlaybackGroup task.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#define FIREBOLT_RIALTO_SERVER_TASKS_GENERIC_FIRST_FRAME_RECEIVED_H_

#include "GenericPlayerContext.h"
#include "GstPlayerTypes.h"
#include "IGstGenericPlayerClient.h"
#include "IGstGenericPlayerPrivate.h"
#include "IPlayerTask.h"
Expand All @@ -31,7 +32,8 @@ class FirstFrameReceived : public IPlayerTask
{
public:
FirstFrameReceived(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, IGstGenericPlayerClient *client,
MediaSourceType sourceType);
MediaSourceType sourceType,
AudioFirstFrameAction audioAction = AudioFirstFrameAction::CLEAR_PROBE);
~FirstFrameReceived() override;

void execute() const override;
Expand All @@ -41,6 +43,7 @@ class FirstFrameReceived : public IPlayerTask
IGstGenericPlayerPrivate &m_player;
IGstGenericPlayerClient *m_gstPlayerClient;
MediaSourceType m_sourceType;
AudioFirstFrameAction m_audioAction;
};
} // namespace firebolt::rialto::server::tasks::generic

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ class GenericPlayerTaskFactory : public IGenericPlayerTaskFactory
IGstGenericPlayerPrivate &player) const override;
std::unique_ptr<IPlayerTask> createUnderflow(GenericPlayerContext &context, IGstGenericPlayerPrivate &player,
bool underflowEnable, MediaSourceType sourceType) const override;
std::unique_ptr<IPlayerTask> createFirstFrameReceived(GenericPlayerContext &context, IGstGenericPlayerPrivate &player,
MediaSourceType sourceType) const override;
std::unique_ptr<IPlayerTask>
createFirstFrameReceived(GenericPlayerContext &context, IGstGenericPlayerPrivate &player, MediaSourceType sourceType,
AudioFirstFrameAction audioAction = AudioFirstFrameAction::CLEAR_PROBE) const override;
std::unique_ptr<IPlayerTask> createUpdatePlaybackGroup(GenericPlayerContext &context,
IGstGenericPlayerPrivate &player, GstElement *typefind,
const GstCaps *caps) const override;
Expand Down
78 changes: 69 additions & 9 deletions media/server/gstplayer/source/GstGenericPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "TypeConverters.h"
#include "Utils.h"
#include "WorkerThread.h"
#include "tasks/generic/FirstFrameReceived.h"
#include "tasks/generic/GenericPlayerTaskFactory.h"
Comment on lines 38 to 40
Comment on lines 37 to 40

namespace
Expand Down Expand Up @@ -310,6 +311,7 @@ void GstGenericPlayer::termPipeline()
}

m_finishSourceSetupTimer.reset();
clearAudioFirstFrameFallbackProbe();

for (auto &elem : m_context.streamInfo)
{
Expand Down Expand Up @@ -1514,6 +1516,8 @@ bool GstGenericPlayer::setCodecData(GstCaps *caps, const std::shared_ptr<CodecDa

void GstGenericPlayer::pushSampleIfRequired(GstElement *source, const MediaSourceType &mediaSourceType)
{
const std::string kTypeStr{common::convertMediaSourceType(mediaSourceType)};

auto initialPosition = m_context.initialPositions.find(source);
if (m_context.initialPositions.end() == initialPosition)
{
Expand All @@ -1528,7 +1532,7 @@ void GstGenericPlayer::pushSampleIfRequired(GstElement *source, const MediaSourc
for (const auto &[position, resetTime, appliedRate, stopPosition] : initialPosition->second)
{
GstSeekFlags seekFlag = resetTime ? GST_SEEK_FLAG_FLUSH : GST_SEEK_FLAG_NONE;
RIALTO_SERVER_LOG_DEBUG("Pushing new %s sample...", common::convertMediaSourceType(mediaSourceType));
RIALTO_SERVER_LOG_DEBUG("Pushing new %s sample...", kTypeStr.c_str());
GstSegment *segment{m_gstWrapper->gstSegmentNew()};
m_gstWrapper->gstSegmentInit(segment, GST_FORMAT_TIME);
if (!m_gstWrapper->gstSegmentDoSeek(segment, m_context.playbackRate, GST_FORMAT_TIME, seekFlag,
Expand All @@ -1542,10 +1546,9 @@ void GstGenericPlayer::pushSampleIfRequired(GstElement *source, const MediaSourc
segment->applied_rate = appliedRate;
RIALTO_SERVER_LOG_MIL("New %s segment: [%" GST_TIME_FORMAT ", %" GST_TIME_FORMAT
"], rate: %f, appliedRate %f, reset_time: %d\n",
common::convertMediaSourceType(mediaSourceType), GST_TIME_ARGS(segment->start),
GST_TIME_ARGS(segment->stop), segment->rate, segment->applied_rate, resetTime);
auto recordId = m_context.gstProfiler->createRecord("First Segment Received",
common::convertMediaSourceType(mediaSourceType));
kTypeStr.c_str(), GST_TIME_ARGS(segment->start), GST_TIME_ARGS(segment->stop),
segment->rate, segment->applied_rate, resetTime);
auto recordId = m_context.gstProfiler->createRecord("First Segment Received", kTypeStr);
if (recordId)
m_context.gstProfiler->logRecord(recordId.value());

Expand All @@ -1560,7 +1563,7 @@ void GstGenericPlayer::pushSampleIfRequired(GstElement *source, const MediaSourc

m_gstWrapper->gstSegmentFree(segment);

if (MediaSourceType::AUDIO == mediaSourceType)
if (mediaSourceType == MediaSourceType::AUDIO)
{
m_context.audioGstSegmentPosition = position;
}
Expand Down Expand Up @@ -1752,6 +1755,63 @@ void GstGenericPlayer::scheduleFirstVideoFrameReceived()
}
}

void GstGenericPlayer::scheduleFirstAudioFrameReceived(AudioFirstFrameAction audioAction)
{
if (m_workerThread)
{
m_workerThread->enqueueTask(
m_taskFactory->createFirstFrameReceived(m_context, *this, MediaSourceType::AUDIO, audioAction));
}
}

void GstGenericPlayer::setAudioFirstFrameFallbackProbe(GstPad *pad, gulong id)
{
GstPad *oldPad{m_context.audioFirstFrameProbePad};
gulong oldId{m_context.audioFirstFrameProbeId};
m_context.audioFirstFrameProbePad = pad;
m_context.audioFirstFrameProbeId = id;

if (oldPad && oldId != 0)
{
m_gstWrapper->gstPadRemoveProbe(oldPad, oldId);
}

if (oldPad)
{
m_gstWrapper->gstObjectUnref(oldPad);
}
}
Comment thread
Copilot marked this conversation as resolved.
Comment thread
skywojciechowskim marked this conversation as resolved.

void GstGenericPlayer::clearAudioFirstFrameFallbackProbe()
{
GstPad *pad{m_context.audioFirstFrameProbePad};
gulong id{m_context.audioFirstFrameProbeId};
m_context.audioFirstFrameProbePad = nullptr;
m_context.audioFirstFrameProbeId = 0;

if (pad && id != 0)
{
m_gstWrapper->gstPadRemoveProbe(pad, id);
}

if (pad)
{
m_gstWrapper->gstObjectUnref(pad);
}
}
Comment thread
Koky2701 marked this conversation as resolved.

void GstGenericPlayer::clearAudioFirstFrameFallbackProbeState()
{
GstPad *pad{m_context.audioFirstFrameProbePad};
m_context.audioFirstFrameProbePad = nullptr;
m_context.audioFirstFrameProbeId = 0;

if (pad)
{
m_gstWrapper->gstObjectUnref(pad);
}
}
Comment thread
Copilot marked this conversation as resolved.

void GstGenericPlayer::scheduleAllSourcesAttached()
{
allSourcesAttached();
Expand Down Expand Up @@ -2324,9 +2384,9 @@ void GstGenericPlayer::startNotifyPlaybackInfoTimer()

notifyPlaybackInfo();

m_playbackInfoTimer =
m_timerFactory
->createTimer(kPlaybackInfoTimerMs, [this]() { notifyPlaybackInfo(); }, firebolt::rialto::common::TimerType::PERIODIC);
const auto kNotifyPlaybackInfo = [this]() { notifyPlaybackInfo(); };
m_playbackInfoTimer = m_timerFactory->createTimer(kPlaybackInfoTimerMs, kNotifyPlaybackInfo,
firebolt::rialto::common::TimerType::PERIODIC);
}

void GstGenericPlayer::stopNotifyPlaybackInfoTimer()
Expand Down
2 changes: 1 addition & 1 deletion media/server/gstplayer/source/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
namespace
{
const char *underflowSignals[]{"buffer-underflow-callback", "vidsink-underflow-callback", "underrun-callback"};
const char *firstFrameSignals[]{"first-video-frame-callback"};
const char *firstFrameSignals[]{"first-video-frame-callback", "first-audio-frame", "first-audio-frame-callback"};
Comment thread
Koky2701 marked this conversation as resolved.

Comment thread
skywojciechowskim marked this conversation as resolved.
Comment on lines 31 to 33
bool isType(const firebolt::rialto::wrappers::IGstWrapper &gstWrapper, GstElement *element, GstElementFactoryListType type)
{
Expand Down
2 changes: 2 additions & 0 deletions media/server/gstplayer/source/tasks/generic/AttachSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ void AttachSource::addSource() const

void AttachSource::reattachAudioSource() const
{
m_context.firstAudioFrameReceived = false;

Comment thread
Koky2701 marked this conversation as resolved.
if (!m_player.reattachSource(m_attachedSource))
Comment thread
Copilot marked this conversation as resolved.
{
RIALTO_SERVER_LOG_ERROR("Reattaching source failed!");
Expand Down
34 changes: 31 additions & 3 deletions media/server/gstplayer/source/tasks/generic/FirstFrameReceived.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
namespace firebolt::rialto::server::tasks::generic
{
FirstFrameReceived::FirstFrameReceived(GenericPlayerContext &context, IGstGenericPlayerPrivate &player,
IGstGenericPlayerClient *client, MediaSourceType sourceType)
: m_context{context}, m_player{player}, m_gstPlayerClient{client}, m_sourceType{sourceType}
IGstGenericPlayerClient *client, MediaSourceType sourceType,
AudioFirstFrameAction audioAction)
: m_context{context}, m_player{player}, m_gstPlayerClient{client}, m_sourceType{sourceType},
m_audioAction{audioAction}
{
RIALTO_SERVER_LOG_DEBUG("Constructing FirstFrameReceived");
}
Expand All @@ -37,7 +39,33 @@ FirstFrameReceived::~FirstFrameReceived()

void FirstFrameReceived::execute() const
{
RIALTO_SERVER_LOG_WARN("Executing FirstFrameReceived for %s source", common::convertMediaSourceType(m_sourceType));
RIALTO_SERVER_LOG_DEBUG("Executing FirstFrameReceived for %s source", common::convertMediaSourceType(m_sourceType));

if (m_sourceType == MediaSourceType::AUDIO)
{
if (m_audioAction == AudioFirstFrameAction::CLEAR_PROBE)
{
m_player.clearAudioFirstFrameFallbackProbe();
}
else if (m_audioAction == AudioFirstFrameAction::CLEAR_PROBE_STATE)
{
m_player.clearAudioFirstFrameFallbackProbeState();
}

if (m_context.audioSourceRemoved)
{
RIALTO_SERVER_LOG_DEBUG("Ignoring first audio frame notification - audio source removed");
return;
}

if (m_context.firstAudioFrameReceived)
{
RIALTO_SERVER_LOG_DEBUG("First audio frame notification already sent");
return;
}

m_context.firstAudioFrameReceived = true;
}

if (m_gstPlayerClient)
{
Expand Down
6 changes: 6 additions & 0 deletions media/server/gstplayer/source/tasks/generic/Flush.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ void Flush::execute() const
streamInfo.buffers.clear();
m_context.initialPositions.erase(sourceElem->second.appSrc);

if (m_type == MediaSourceType::AUDIO)
{
m_player.clearAudioFirstFrameFallbackProbe();
m_context.firstAudioFrameReceived = false;
}
Comment on lines +75 to +79

m_gstPlayerClient->invalidateActiveRequests(m_type);

if (GST_STATE(m_context.pipeline) >= GST_STATE_PAUSED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,10 @@ std::unique_ptr<IPlayerTask> GenericPlayerTaskFactory::createUnderflow(GenericPl

std::unique_ptr<IPlayerTask> GenericPlayerTaskFactory::createFirstFrameReceived(GenericPlayerContext &context,
IGstGenericPlayerPrivate &player,
MediaSourceType sourceType) const
MediaSourceType sourceType,
AudioFirstFrameAction audioAction) const
{
return std::make_unique<tasks::generic::FirstFrameReceived>(context, player, m_client, sourceType);
return std::make_unique<tasks::generic::FirstFrameReceived>(context, player, m_client, sourceType, audioAction);
}

std::unique_ptr<IPlayerTask> GenericPlayerTaskFactory::createUpdatePlaybackGroup(GenericPlayerContext &context,
Expand Down
Loading
Loading