mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-12 03:16:33 +00:00
f7ce5cd694
The former properly encodes the URL components to give a valid URL, the latter generates a human-readable URL.
953 lines
21 KiB
C++
953 lines
21 KiB
C++
/* GStreamer
|
|
*
|
|
* Copyright (C) 2015 Alexandre Moreno <alexmorenocano@gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "qgstplayer.h"
|
|
#include <QDebug>
|
|
#include <QSize>
|
|
#include <QMetaObject>
|
|
#include <functional>
|
|
#include <QThread>
|
|
#include <QtAlgorithms>
|
|
#include <QImage>
|
|
|
|
#include <gst/gst.h>
|
|
#include <gst/tag/tag.h>
|
|
|
|
namespace QGstPlayer {
|
|
|
|
class RegisterMetaTypes
|
|
{
|
|
public:
|
|
RegisterMetaTypes()
|
|
{
|
|
qRegisterMetaType<Player::State>("State");
|
|
}
|
|
|
|
} _register;
|
|
|
|
MediaInfo::MediaInfo(Player *player)
|
|
: QObject(player)
|
|
, uri_()
|
|
, title_()
|
|
, isSeekable_(false)
|
|
, videoStreams_()
|
|
, audioStreams_()
|
|
, subtitleStreams_()
|
|
, sample_()
|
|
{
|
|
|
|
}
|
|
|
|
QString MediaInfo::uri() const
|
|
{
|
|
return uri_;
|
|
}
|
|
|
|
QString MediaInfo::title() const
|
|
{
|
|
return title_;
|
|
}
|
|
|
|
bool MediaInfo::isSeekable() const
|
|
{
|
|
return isSeekable_;
|
|
}
|
|
|
|
const QList<QObject *> &MediaInfo::videoStreams() const
|
|
{
|
|
return videoStreams_;
|
|
}
|
|
|
|
const QList<QObject *> &MediaInfo::audioStreams() const
|
|
{
|
|
return audioStreams_;
|
|
}
|
|
|
|
const QList<QObject*> &MediaInfo::subtitleStreams() const
|
|
{
|
|
return subtitleStreams_;
|
|
}
|
|
|
|
const QImage &MediaInfo::sample()
|
|
{
|
|
return sample_;
|
|
}
|
|
|
|
void MediaInfo::update(GstPlayerMediaInfo *info)
|
|
{
|
|
Q_ASSERT(info != 0);
|
|
|
|
// FIXME since media-info signal gets emitted
|
|
// several times, we just update info iff the
|
|
// media uri has changed.
|
|
if (uri_ == gst_player_media_info_get_uri(info)) {
|
|
return;
|
|
}
|
|
|
|
uri_ = QString(gst_player_media_info_get_uri(info));
|
|
|
|
title_ = QString::fromLocal8Bit(gst_player_media_info_get_title(info));
|
|
|
|
// if media has no title, return the file name
|
|
if (title_.isEmpty()) {
|
|
QUrl url(gst_player_media_info_get_uri(info));
|
|
title_ = url.fileName();
|
|
}
|
|
|
|
emit titleChanged();
|
|
|
|
if (isSeekable_ != gst_player_media_info_is_seekable(info)) {
|
|
isSeekable_ = !isSeekable_;
|
|
emit seekableChanged();
|
|
}
|
|
|
|
if (!subtitleStreams_.isEmpty()) {
|
|
qDeleteAll(subtitleStreams_);
|
|
subtitleStreams_.clear();
|
|
}
|
|
|
|
if (!videoStreams_.isEmpty()) {
|
|
qDeleteAll(videoStreams_);
|
|
videoStreams_.clear();
|
|
}
|
|
|
|
if (!audioStreams_.isEmpty()) {
|
|
qDeleteAll(audioStreams_);
|
|
audioStreams_.clear();
|
|
}
|
|
|
|
GList *list = gst_player_get_subtitle_streams(info);
|
|
|
|
g_list_foreach (list, [](gpointer data, gpointer user_data) {
|
|
GstPlayerSubtitleInfo *info = static_cast<GstPlayerSubtitleInfo*>(data);
|
|
QList<QObject*> *subs = static_cast<QList<QObject*>*>(user_data);
|
|
|
|
subs->append(new SubtitleInfo(info));
|
|
}, &subtitleStreams_);
|
|
|
|
list = gst_player_get_video_streams(info);
|
|
|
|
g_list_foreach (list, [](gpointer data, gpointer user_data) {
|
|
GstPlayerVideoInfo *info = static_cast<GstPlayerVideoInfo*>(data);
|
|
QList<QObject*> *videos = static_cast<QList<QObject*>*>(user_data);
|
|
|
|
videos->append(new VideoInfo(info));
|
|
}, &videoStreams_);
|
|
|
|
list = gst_player_get_audio_streams(info);
|
|
|
|
g_list_foreach (list, [](gpointer data, gpointer user_data) {
|
|
GstPlayerAudioInfo *info = static_cast<GstPlayerAudioInfo*>(data);
|
|
QList<QObject*> *audios = static_cast<QList<QObject*>*>(user_data);
|
|
|
|
audios->append(new AudioInfo(info));
|
|
}, &audioStreams_);
|
|
|
|
GstSample *sample;
|
|
GstMapInfo map_info;
|
|
GstBuffer *buffer;
|
|
const GstStructure *caps_struct;
|
|
GstTagImageType type = GST_TAG_IMAGE_TYPE_UNDEFINED;
|
|
|
|
/* get image sample buffer from media */
|
|
sample = gst_player_media_info_get_image_sample (info);
|
|
if (!sample)
|
|
return;
|
|
|
|
buffer = gst_sample_get_buffer (sample);
|
|
caps_struct = gst_sample_get_info (sample);
|
|
|
|
/* if sample is retrieved from preview-image tag then caps struct
|
|
* will not be defined. */
|
|
if (caps_struct)
|
|
gst_structure_get_enum (caps_struct, "image-type",
|
|
GST_TYPE_TAG_IMAGE_TYPE, reinterpret_cast<gint*>(&type));
|
|
|
|
/* FIXME: Should we check more type ?? */
|
|
if ((type != GST_TAG_IMAGE_TYPE_FRONT_COVER) &&
|
|
(type != GST_TAG_IMAGE_TYPE_UNDEFINED) &&
|
|
(type != GST_TAG_IMAGE_TYPE_NONE)) {
|
|
g_print ("unsupport type ... %d \n", type);
|
|
return;
|
|
}
|
|
|
|
if (!gst_buffer_map (buffer, &map_info, GST_MAP_READ)) {
|
|
g_print ("failed to map gst buffer \n");
|
|
return;
|
|
}
|
|
|
|
sample_ = QImage::fromData(map_info.data, map_info.size);
|
|
if (sample_.isNull())
|
|
qWarning() << "failed to load media info sample image";
|
|
|
|
emit sampleChanged();
|
|
|
|
gst_buffer_unmap (buffer, &map_info);
|
|
}
|
|
|
|
VideoInfo::VideoInfo(GstPlayerVideoInfo *info)
|
|
: StreamInfo(reinterpret_cast<GstPlayerStreamInfo*>(info))
|
|
, video_(info)
|
|
, resolution_(gst_player_video_info_get_width(info), gst_player_video_info_get_height(info))
|
|
{
|
|
|
|
}
|
|
|
|
QSize VideoInfo::resolution() const
|
|
{
|
|
return resolution_;
|
|
}
|
|
|
|
AudioInfo::AudioInfo(GstPlayerAudioInfo *info)
|
|
: StreamInfo(reinterpret_cast<GstPlayerStreamInfo*>(info))
|
|
, audio_(info)
|
|
, language_(gst_player_audio_info_get_language(info))
|
|
, channels_(gst_player_audio_info_get_channels(info))
|
|
, bitRate_(gst_player_audio_info_get_bitrate(info))
|
|
, sampleRate_(gst_player_audio_info_get_sample_rate(info))
|
|
{
|
|
|
|
}
|
|
|
|
const QString &AudioInfo::language() const
|
|
{
|
|
return language_;
|
|
}
|
|
|
|
int AudioInfo::channels() const
|
|
{
|
|
return channels_;
|
|
}
|
|
|
|
int AudioInfo::bitRate() const
|
|
{
|
|
return bitRate_;
|
|
}
|
|
|
|
int AudioInfo::sampleRate() const
|
|
{
|
|
return sampleRate_;
|
|
}
|
|
|
|
SubtitleInfo::SubtitleInfo(GstPlayerSubtitleInfo *info)
|
|
: StreamInfo(reinterpret_cast<GstPlayerStreamInfo*>(info))
|
|
, subtitle_(info)
|
|
, language_(gst_player_subtitle_info_get_language(info))
|
|
{
|
|
|
|
}
|
|
|
|
const QString &SubtitleInfo::language() const
|
|
{
|
|
return language_;
|
|
}
|
|
|
|
int StreamInfo::index() const
|
|
{
|
|
return index_;
|
|
}
|
|
|
|
StreamInfo::StreamInfo(GstPlayerStreamInfo *info)
|
|
: stream_(info)
|
|
, index_(gst_player_stream_info_get_index(info))
|
|
{
|
|
|
|
}
|
|
|
|
bool Player::isVideoAvailable() const
|
|
{
|
|
GstPlayerVideoInfo *video_info;
|
|
|
|
video_info = gst_player_get_current_video_track (player_);
|
|
if (video_info) {
|
|
g_object_unref (video_info);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
MediaInfo *Player::mediaInfo() const
|
|
{
|
|
return mediaInfo_;
|
|
}
|
|
|
|
QVariant Player::currentVideo() const
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
GstPlayerVideoInfo *track = gst_player_get_current_video_track(player_);
|
|
|
|
if (!track)
|
|
return QVariant();
|
|
|
|
return QVariant::fromValue(new VideoInfo(track));
|
|
}
|
|
|
|
QVariant Player::currentAudio() const
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
GstPlayerAudioInfo *track = gst_player_get_current_audio_track(player_);
|
|
|
|
if (!track)
|
|
return QVariant();
|
|
|
|
return QVariant::fromValue(new AudioInfo(track));
|
|
}
|
|
|
|
QVariant Player::currentSubtitle() const
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
GstPlayerSubtitleInfo *track = gst_player_get_current_subtitle_track(player_);
|
|
|
|
if (!track)
|
|
return QVariant();
|
|
|
|
return QVariant::fromValue(new SubtitleInfo(track));
|
|
}
|
|
|
|
void Player::setCurrentVideo(QVariant track)
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
VideoInfo* info = track.value<VideoInfo*>();
|
|
Q_ASSERT(info);
|
|
|
|
gst_player_set_video_track(player_, info->index());
|
|
}
|
|
|
|
void Player::setCurrentAudio(QVariant track)
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
AudioInfo* info = track.value<AudioInfo*>();
|
|
Q_ASSERT(info);
|
|
|
|
gst_player_set_audio_track(player_, info->index());
|
|
|
|
}
|
|
|
|
void Player::setCurrentSubtitle(QVariant track)
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
SubtitleInfo* info = track.value<SubtitleInfo*>();
|
|
Q_ASSERT(info);
|
|
|
|
gst_player_set_subtitle_track(player_, info->index());
|
|
}
|
|
|
|
bool Player::isSubtitleEnabled() const
|
|
{
|
|
return subtitleEnabled_;
|
|
}
|
|
|
|
void Player::setSubtitleEnabled(bool enabled)
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
subtitleEnabled_ = enabled;
|
|
|
|
gst_player_set_subtitle_track_enabled(player_, enabled);
|
|
|
|
emit subtitleEnabledChanged(enabled);
|
|
}
|
|
|
|
Player::Player(QObject *parent, VideoRenderer *renderer)
|
|
: QObject(parent)
|
|
, player_()
|
|
, state_(STOPPED)
|
|
, videoDimensions_(QSize())
|
|
, mediaInfo_()
|
|
, videoAvailable_(false)
|
|
, subtitleEnabled_(false)
|
|
, autoPlay_(false)
|
|
{
|
|
|
|
player_ = gst_player_new(renderer ? renderer->renderer() : 0,
|
|
gst_player_qt_signal_dispatcher_new(this));
|
|
|
|
g_object_connect(player_,
|
|
"swapped-signal::state-changed", G_CALLBACK (Player::onStateChanged), this,
|
|
"swapped-signal::position-updated", G_CALLBACK (Player::onPositionUpdated), this,
|
|
"swapped-signal::duration-changed", G_CALLBACK (Player::onDurationChanged), this,
|
|
"swapped-signal::buffering", G_CALLBACK (Player::onBufferingChanged), this,
|
|
"swapped-signal::video-dimensions-changed", G_CALLBACK (Player::onVideoDimensionsChanged), this,
|
|
"swapped-signal::volume-changed", G_CALLBACK (Player::onVolumeChanged), this,
|
|
"swapped-signal::mute-changed", G_CALLBACK (Player::onMuteChanged), this,
|
|
"swapped-signal::media-info-updated", G_CALLBACK (Player::onMediaInfoUpdated), this,
|
|
"swapped-signal::end-of-stream", G_CALLBACK (Player::onEndOfStreamReached), this, NULL);
|
|
|
|
mediaInfo_ = new MediaInfo(this);
|
|
gst_player_set_subtitle_track_enabled(player_, false);
|
|
}
|
|
|
|
Player::~Player()
|
|
{
|
|
if (player_) {
|
|
g_signal_handlers_disconnect_by_data(player_, this);
|
|
gst_player_stop(player_);
|
|
g_object_unref(player_);
|
|
}
|
|
}
|
|
|
|
void
|
|
Player::onStateChanged(Player * player, GstPlayerState state)
|
|
{
|
|
player->state_ = static_cast<Player::State>(state);
|
|
|
|
emit player->stateChanged(player->state_);
|
|
}
|
|
|
|
void
|
|
Player::onPositionUpdated(Player * player, GstClockTime position)
|
|
{
|
|
emit player->positionUpdated(position);
|
|
}
|
|
|
|
void
|
|
Player::onDurationChanged(Player * player, GstClockTime duration)
|
|
{
|
|
emit player->durationChanged(duration);
|
|
}
|
|
|
|
void
|
|
Player::onBufferingChanged(Player * player, int percent)
|
|
{
|
|
emit player->bufferingChanged(percent);
|
|
}
|
|
|
|
void
|
|
Player::onVideoDimensionsChanged(Player * player, int w, int h)
|
|
{
|
|
QSize res(w,h);
|
|
|
|
if (res == player->videoDimensions_)
|
|
return;
|
|
player->videoDimensions_ = res;
|
|
|
|
player->setResolution(res);
|
|
|
|
emit player->resolutionChanged(res);
|
|
}
|
|
|
|
void
|
|
Player::onVolumeChanged(Player *player)
|
|
{
|
|
qreal new_val;
|
|
|
|
new_val = gst_player_get_volume (player->player_);
|
|
|
|
emit player->volumeChanged(new_val);
|
|
}
|
|
|
|
void
|
|
Player::onMuteChanged(Player *player)
|
|
{
|
|
bool new_val;
|
|
|
|
new_val = gst_player_get_mute (player->player_);
|
|
|
|
emit player->mutedChanged(new_val);
|
|
}
|
|
|
|
void
|
|
Player::onMediaInfoUpdated(Player *player, GstPlayerMediaInfo *media_info)
|
|
{
|
|
bool val = player->isVideoAvailable();
|
|
|
|
if (player->videoAvailable_ != val) {
|
|
player->videoAvailable_ = val;
|
|
emit player->videoAvailableChanged(val);
|
|
}
|
|
|
|
player->mediaInfo()->update(media_info);
|
|
|
|
emit player->mediaInfoChanged();
|
|
}
|
|
|
|
void Player::onEndOfStreamReached(Player *player)
|
|
{
|
|
Q_ASSERT(player != 0);
|
|
|
|
emit player->endOfStream();
|
|
}
|
|
|
|
void Player::setUri(QUrl url)
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
QByteArray uri = url.toEncoded();
|
|
|
|
gst_player_set_uri(player_, uri.data());
|
|
|
|
autoPlay_ ? play() : pause();
|
|
|
|
emit sourceChanged(url);
|
|
}
|
|
|
|
QList<QUrl> Player::playlist() const
|
|
{
|
|
return playlist_;
|
|
}
|
|
|
|
void Player::setPlaylist(const QList<QUrl> &playlist)
|
|
{
|
|
if (!playlist_.isEmpty()) {
|
|
playlist_.erase(playlist_.begin(), playlist_.end());
|
|
}
|
|
|
|
playlist_ = playlist;
|
|
|
|
iter_ = playlist_.begin();
|
|
setUri(*iter_);
|
|
}
|
|
|
|
void Player::next()
|
|
{
|
|
if (playlist_.isEmpty())
|
|
return;
|
|
|
|
if (iter_ == playlist_.end())
|
|
return;
|
|
|
|
setUri(*++iter_);
|
|
}
|
|
|
|
void Player::previous()
|
|
{
|
|
if (playlist_.isEmpty())
|
|
return;
|
|
|
|
if (iter_ == playlist_.begin())
|
|
return;
|
|
|
|
setUri(*--iter_);
|
|
}
|
|
|
|
bool Player::autoPlay() const
|
|
{
|
|
return autoPlay_;
|
|
}
|
|
|
|
void Player::setAutoPlay(bool auto_play)
|
|
{
|
|
autoPlay_ = auto_play;
|
|
|
|
if (autoPlay_) {
|
|
connect(this, SIGNAL(endOfStream()), SLOT(next()));
|
|
}
|
|
}
|
|
|
|
QUrl Player::source() const
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
QString url = QString::fromLocal8Bit(gst_player_get_uri(player_));
|
|
|
|
return QUrl(url);
|
|
}
|
|
|
|
qint64 Player::duration() const
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
return gst_player_get_duration(player_);
|
|
}
|
|
|
|
qint64 Player::position() const
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
return gst_player_get_position(player_);
|
|
}
|
|
|
|
qreal Player::volume() const
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
return gst_player_get_volume(player_);
|
|
}
|
|
|
|
bool Player::isMuted() const
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
return gst_player_get_mute(player_);
|
|
}
|
|
|
|
int Player::buffering() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
QSize Player::resolution() const
|
|
{
|
|
return videoDimensions_;
|
|
}
|
|
|
|
void Player::setResolution(QSize size)
|
|
{
|
|
videoDimensions_ = size;
|
|
}
|
|
|
|
Player::State Player::state() const
|
|
{
|
|
return state_;
|
|
}
|
|
|
|
GstElement *Player::pipeline() const
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
return gst_player_get_pipeline(player_);
|
|
}
|
|
|
|
void Player::play()
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
gst_player_play(player_);
|
|
}
|
|
|
|
void Player::pause()
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
gst_player_pause(player_);
|
|
}
|
|
|
|
void Player::stop()
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
gst_player_stop(player_);
|
|
}
|
|
|
|
void Player::seek(qint64 position)
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
gst_player_seek(player_, position);
|
|
}
|
|
|
|
void Player::setSource(QUrl const& url)
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
// discard playlist
|
|
if (!playlist_.isEmpty()) {
|
|
playlist_.erase(playlist_.begin(), playlist_.end());
|
|
}
|
|
|
|
setUri(url);
|
|
|
|
emit sourceChanged(url);
|
|
}
|
|
|
|
void Player::setVolume(qreal val)
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
gst_player_set_volume(player_, val);
|
|
}
|
|
|
|
void Player::setMuted(bool val)
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
gst_player_set_mute(player_, val);
|
|
}
|
|
|
|
void Player::setPosition(qint64 pos)
|
|
{
|
|
Q_ASSERT(player_ != 0);
|
|
|
|
gst_player_seek(player_, pos);
|
|
}
|
|
|
|
GstPlayerVideoRenderer *VideoRenderer::renderer()
|
|
{
|
|
return static_cast<GstPlayerVideoRenderer*> (gst_object_ref (renderer_));
|
|
}
|
|
|
|
VideoRenderer::VideoRenderer()
|
|
{
|
|
renderer_ = static_cast<GstPlayerVideoRenderer*>
|
|
(g_object_new (GST_TYPE_PLAYER_QT_VIDEO_RENDERER, "renderer", this, NULL));
|
|
}
|
|
|
|
VideoRenderer::~VideoRenderer()
|
|
{
|
|
if (renderer_) gst_object_unref(renderer_);
|
|
}
|
|
|
|
}
|
|
|
|
struct _GstPlayerQtVideoRenderer
|
|
{
|
|
GObject parent;
|
|
|
|
gpointer renderer;
|
|
};
|
|
|
|
struct _GstPlayerQtVideoRendererClass
|
|
{
|
|
GObjectClass parent_class;
|
|
};
|
|
|
|
static void
|
|
gst_player_qt_video_renderer_interface_init
|
|
(GstPlayerVideoRendererInterface * iface);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GstPlayerQtVideoRenderer,
|
|
gst_player_qt_video_renderer, G_TYPE_OBJECT,
|
|
G_IMPLEMENT_INTERFACE (GST_TYPE_PLAYER_VIDEO_RENDERER,
|
|
gst_player_qt_video_renderer_interface_init))
|
|
|
|
enum
|
|
{
|
|
QT_VIDEO_RENDERER_PROP_0,
|
|
QT_VIDEO_RENDERER_PROP_RENDERER,
|
|
QT_VIDEO_RENDERER_PROP_LAST
|
|
};
|
|
|
|
static GParamSpec * qt_video_renderer_param_specs
|
|
[QT_VIDEO_RENDERER_PROP_LAST] = { NULL, };
|
|
|
|
static void
|
|
gst_player_qt_video_renderer_set_property (GObject * object,
|
|
guint prop_id, const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstPlayerQtVideoRenderer *self = GST_PLAYER_QT_VIDEO_RENDERER (object);
|
|
|
|
switch (prop_id) {
|
|
case QT_VIDEO_RENDERER_PROP_RENDERER:
|
|
self->renderer = g_value_get_pointer (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_player_qt_video_renderer_get_property (GObject * object,
|
|
guint prop_id, GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstPlayerQtVideoRenderer *self = GST_PLAYER_QT_VIDEO_RENDERER (object);
|
|
|
|
switch (prop_id) {
|
|
case QT_VIDEO_RENDERER_PROP_RENDERER:
|
|
g_value_set_pointer (value, self->renderer);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_player_qt_video_renderer_finalize (GObject * object)
|
|
{
|
|
G_OBJECT_CLASS
|
|
(gst_player_qt_video_renderer_parent_class)->finalize(object);
|
|
}
|
|
|
|
static void
|
|
gst_player_qt_video_renderer_class_init
|
|
(GstPlayerQtVideoRendererClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->set_property =
|
|
gst_player_qt_video_renderer_set_property;
|
|
gobject_class->get_property =
|
|
gst_player_qt_video_renderer_get_property;
|
|
gobject_class->finalize = gst_player_qt_video_renderer_finalize;
|
|
|
|
qt_video_renderer_param_specs
|
|
[QT_VIDEO_RENDERER_PROP_RENDERER] =
|
|
g_param_spec_pointer ("renderer", "Qt Renderer", "",
|
|
static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_properties (gobject_class,
|
|
QT_VIDEO_RENDERER_PROP_LAST,
|
|
qt_video_renderer_param_specs);
|
|
}
|
|
|
|
static void
|
|
gst_player_qt_video_renderer_init (G_GNUC_UNUSED GstPlayerQtVideoRenderer * self)
|
|
{
|
|
|
|
}
|
|
|
|
static GstElement *
|
|
gst_player_qt_video_renderer_create_video_sink
|
|
(GstPlayerVideoRenderer * iface, G_GNUC_UNUSED GstPlayer *player)
|
|
{
|
|
GstPlayerQtVideoRenderer *self = GST_PLAYER_QT_VIDEO_RENDERER (iface);
|
|
|
|
g_assert(self->renderer != NULL);
|
|
|
|
return static_cast<QGstPlayer::VideoRenderer*>(self->renderer)->createVideoSink();
|
|
}
|
|
|
|
static void
|
|
gst_player_qt_video_renderer_interface_init
|
|
(GstPlayerVideoRendererInterface * iface)
|
|
{
|
|
iface->create_video_sink = gst_player_qt_video_renderer_create_video_sink;
|
|
}
|
|
|
|
struct _GstPlayerQtSignalDispatcher
|
|
{
|
|
GObject parent;
|
|
|
|
gpointer player;
|
|
};
|
|
|
|
struct _GstPlayerQtSignalDispatcherClass
|
|
{
|
|
GObjectClass parent_class;
|
|
};
|
|
|
|
static void
|
|
gst_player_qt_signal_dispatcher_interface_init
|
|
(GstPlayerSignalDispatcherInterface * iface);
|
|
|
|
enum
|
|
{
|
|
QT_SIGNAL_DISPATCHER_PROP_0,
|
|
QT_SIGNAL_DISPATCHER_PROP_PLAYER,
|
|
QT_SIGNAL_DISPATCHER_PROP_LAST
|
|
};
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GstPlayerQtSignalDispatcher,
|
|
gst_player_qt_signal_dispatcher, G_TYPE_OBJECT,
|
|
G_IMPLEMENT_INTERFACE (GST_TYPE_PLAYER_SIGNAL_DISPATCHER,
|
|
gst_player_qt_signal_dispatcher_interface_init));
|
|
|
|
static GParamSpec
|
|
* qt_signal_dispatcher_param_specs
|
|
[QT_SIGNAL_DISPATCHER_PROP_LAST] = { NULL, };
|
|
|
|
static void
|
|
gst_player_qt_signal_dispatcher_finalize (GObject * object)
|
|
{
|
|
G_OBJECT_CLASS
|
|
(gst_player_qt_signal_dispatcher_parent_class)->finalize
|
|
(object);
|
|
}
|
|
|
|
static void
|
|
gst_player_qt_signal_dispatcher_set_property (GObject * object,
|
|
guint prop_id, const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstPlayerQtSignalDispatcher *self =
|
|
GST_PLAYER_QT_SIGNAL_DISPATCHER (object);
|
|
|
|
switch (prop_id) {
|
|
case QT_SIGNAL_DISPATCHER_PROP_PLAYER:
|
|
self->player = g_value_get_pointer (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_player_qt_signal_dispatcher_get_property (GObject * object,
|
|
guint prop_id, GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstPlayerQtSignalDispatcher *self =
|
|
GST_PLAYER_QT_SIGNAL_DISPATCHER (object);
|
|
|
|
switch (prop_id) {
|
|
case QT_SIGNAL_DISPATCHER_PROP_PLAYER:
|
|
g_value_set_pointer (value, self->player);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_player_qt_signal_dispatcher_class_init
|
|
(GstPlayerQtSignalDispatcherClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->finalize =
|
|
gst_player_qt_signal_dispatcher_finalize;
|
|
gobject_class->set_property =
|
|
gst_player_qt_signal_dispatcher_set_property;
|
|
gobject_class->get_property =
|
|
gst_player_qt_signal_dispatcher_get_property;
|
|
|
|
qt_signal_dispatcher_param_specs
|
|
[QT_SIGNAL_DISPATCHER_PROP_PLAYER] =
|
|
g_param_spec_pointer ("player", "Player instance", "",
|
|
static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_properties (gobject_class,
|
|
QT_SIGNAL_DISPATCHER_PROP_LAST,
|
|
qt_signal_dispatcher_param_specs);
|
|
}
|
|
|
|
static void
|
|
gst_player_qt_signal_dispatcher_init
|
|
(G_GNUC_UNUSED GstPlayerQtSignalDispatcher * self)
|
|
{
|
|
|
|
}
|
|
|
|
static void
|
|
gst_player_qt_signal_dispatcher_dispatch (GstPlayerSignalDispatcher
|
|
* iface, G_GNUC_UNUSED GstPlayer * player, void (*emitter) (gpointer data), gpointer data,
|
|
GDestroyNotify destroy)
|
|
{
|
|
GstPlayerQtSignalDispatcher *self = GST_PLAYER_QT_SIGNAL_DISPATCHER (iface);
|
|
QObject dispatch;
|
|
QObject *receiver = static_cast<QObject*>(self->player);
|
|
|
|
QObject::connect(&dispatch, &QObject::destroyed, receiver, [=]() {
|
|
emitter(data);
|
|
if (destroy) destroy(data);
|
|
}, Qt::QueuedConnection);
|
|
}
|
|
|
|
static void
|
|
gst_player_qt_signal_dispatcher_interface_init
|
|
(GstPlayerSignalDispatcherInterface * iface)
|
|
{
|
|
iface->dispatch = gst_player_qt_signal_dispatcher_dispatch;
|
|
}
|
|
|
|
GstPlayerSignalDispatcher *
|
|
gst_player_qt_signal_dispatcher_new (gpointer player)
|
|
{
|
|
return static_cast<GstPlayerSignalDispatcher*>
|
|
(g_object_new (GST_TYPE_PLAYER_QT_SIGNAL_DISPATCHER,
|
|
"player", player, NULL));
|
|
}
|