mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 11:10:37 +00:00
Import GstTranscoder
This commit is contained in:
parent
87311d404e
commit
7a66b16d97
36 changed files with 4262 additions and 1142 deletions
22
data/meson.build
Normal file
22
data/meson.build
Normal file
|
@ -0,0 +1,22 @@
|
|||
encoding_targetsdir = join_paths(get_option('datadir'),
|
||||
'gstreamer-' + api_version, 'encoding-profiles')
|
||||
|
||||
encoding_targets = [
|
||||
['file-extension', ['targets/file-extension/ogv.gep',
|
||||
'targets/file-extension/oga.gep',
|
||||
'targets/file-extension/mkv.gep',
|
||||
'targets/file-extension/mp3.gep',
|
||||
'targets/file-extension/webm.gep',
|
||||
'targets/file-extension/flv.gep',
|
||||
'targets/file-extension/mp4.gep',
|
||||
'targets/file-extension/avi.gep',],
|
||||
],
|
||||
['online-services', ['targets/online-service/youtube.gep',]],
|
||||
['device', ['targets/device/dvd.gep',]],
|
||||
]
|
||||
|
||||
foreach path_targets : encoding_targets
|
||||
dir = join_paths(encoding_targetsdir, path_targets.get(0))
|
||||
etargets = path_targets.get(1)
|
||||
install_data(sources: etargets, install_dir: dir)
|
||||
endforeach
|
24
data/targets/device/dvd.gep
Normal file
24
data/targets/device/dvd.gep
Normal file
|
@ -0,0 +1,24 @@
|
|||
[GStreamer Encoding Target]
|
||||
name=dvd
|
||||
category=device
|
||||
description=Encoding target suitable for DVDs
|
||||
|
||||
[profile-dvd]
|
||||
name=dvd
|
||||
type=container
|
||||
description[c]=This is an encoding profile usable for DVDs
|
||||
format=video/mpeg, mpegversion=(int)2, systemstream=(boolean)true
|
||||
|
||||
[streamprofile-dvd-0]
|
||||
parent=dvd
|
||||
type=video
|
||||
format=video/mpeg, mpegversion=(int)2, systemstream=(boolean)false
|
||||
presence=0
|
||||
pass=0
|
||||
variableframerate=false
|
||||
|
||||
[streamprofile-dvd-1]
|
||||
parent=dvd
|
||||
type=audio
|
||||
format=audio/mpeg, mpegversion=(int)1, layer=(int)2
|
||||
presence=0
|
21
data/targets/file-extension/avi.gep
Normal file
21
data/targets/file-extension/avi.gep
Normal file
|
@ -0,0 +1,21 @@
|
|||
[GStreamer Encoding Target]
|
||||
name=avi
|
||||
category=file-extension
|
||||
description=Default target for files with a .avi extension
|
||||
|
||||
[profile-default]
|
||||
name=default
|
||||
type=container
|
||||
description=Default profile for files with a .avi extension.
|
||||
format=video/x-msvideo
|
||||
|
||||
[streamprofile-default-0]
|
||||
parent=default
|
||||
type=audio
|
||||
format=audio/mpeg, mpegversion=(int)1, mpegaudioversion=(int)1, layer=(int)3
|
||||
|
||||
[streamprofile-default-1]
|
||||
parent=default
|
||||
type=video
|
||||
format=video/mpeg, mpegversion=(int)4
|
||||
|
32
data/targets/file-extension/flv.gep
Normal file
32
data/targets/file-extension/flv.gep
Normal file
|
@ -0,0 +1,32 @@
|
|||
[GStreamer Encoding Target]
|
||||
name=flv
|
||||
category=file-extension
|
||||
description=Default target for files with a .flv extension
|
||||
|
||||
[profile-default]
|
||||
name=default
|
||||
type=container
|
||||
description=Default profile for files with a .flv extension.
|
||||
format=video/x-flv
|
||||
|
||||
[streamprofile-default-0]
|
||||
parent=default
|
||||
type=audio
|
||||
format=audio/mpeg,mpegversion=4
|
||||
|
||||
[streamprofile-default-1]
|
||||
parent=default
|
||||
type=audio
|
||||
format=audio/mpeg, mpegversion=(int)1, mpegaudioversion=(int)1, layer=(int)3
|
||||
|
||||
[streamprofile-default-2]
|
||||
parent=default
|
||||
type=video
|
||||
format=video/x-h264
|
||||
preset=Profile YouTube
|
||||
pass=0
|
||||
|
||||
[streamprofile-default-3]
|
||||
parent=default
|
||||
type=video
|
||||
format=video/x-h264
|
28
data/targets/file-extension/mkv.gep
Normal file
28
data/targets/file-extension/mkv.gep
Normal file
|
@ -0,0 +1,28 @@
|
|||
[GStreamer Encoding Target]
|
||||
name=mkv;matroska;
|
||||
category=file-extension
|
||||
description=Default target for files with a .mkv extension
|
||||
|
||||
[profile-default]
|
||||
name=default
|
||||
description=Default profile for files with a .mkv extension. Audio stream can be either opus (default) or vorbis depending on what is available on the system. Video stream will be either in vp8 (default) or vp9.
|
||||
type=container
|
||||
format=video/x-matroska
|
||||
|
||||
[streamprofile-default-0]
|
||||
parent=default
|
||||
type=audio
|
||||
format=audio/x-vorbis;audio/x-opus
|
||||
|
||||
[streamprofile-default-1]
|
||||
parent=default
|
||||
type=video
|
||||
format=video/x-h264
|
||||
preset=Quality Normal
|
||||
pass=0
|
||||
|
||||
[streamprofile-default-2]
|
||||
parent=default
|
||||
type=video
|
||||
format=video/x-h264
|
||||
pass=0
|
15
data/targets/file-extension/mp3.gep
Normal file
15
data/targets/file-extension/mp3.gep
Normal file
|
@ -0,0 +1,15 @@
|
|||
[GStreamer Encoding Target]
|
||||
name=mp3
|
||||
category=file-extension
|
||||
description=Default target for files with a .mp3 extension
|
||||
|
||||
[profile-default]
|
||||
name=default
|
||||
type=container
|
||||
description=Default profile for files with a .mp3 extension.
|
||||
format=application/x-id3
|
||||
|
||||
[streamprofile-default-0]
|
||||
parent=default
|
||||
type=audio
|
||||
format=audio/mpeg, mpegversion=(int)1, mpegaudioversion=(int)1, layer=(int)3
|
34
data/targets/file-extension/mp4.gep
Normal file
34
data/targets/file-extension/mp4.gep
Normal file
|
@ -0,0 +1,34 @@
|
|||
[GStreamer Encoding Target]
|
||||
name=mp4;mov
|
||||
category=file-extension
|
||||
description=Default target for files with a .mp4 and .mov extension
|
||||
|
||||
[profile-default]
|
||||
name=default
|
||||
type=container
|
||||
description=Default profile for files with a .mp4 extension. Suitable for uploading to youtube.
|
||||
format=video/quicktime
|
||||
preset=Profile YouTube
|
||||
|
||||
[streamprofile-default-0]
|
||||
parent=default
|
||||
type=audio
|
||||
format=audio/mpeg,mpegversion=4,base-profile=lc,rate={48000,96000},channels=2;audio/mpeg,mpegversion=4,base-profile=lc,rate={48000,96000}
|
||||
restriction=audio/x-raw,channels=6,channel-mask=0x3f;audio/x-raw,channels=2
|
||||
|
||||
[streamprofile-default-1]
|
||||
parent=default
|
||||
type=video
|
||||
format=video/x-h264
|
||||
preset=Profile YouTube
|
||||
pass=0
|
||||
|
||||
[streamprofile-default-2]
|
||||
parent=default
|
||||
type=audio
|
||||
format=audio/mpeg,mpegversion=4
|
||||
|
||||
[streamprofile-default-3]
|
||||
parent=default
|
||||
type=video
|
||||
format=video/x-h264
|
15
data/targets/file-extension/oga.gep
Normal file
15
data/targets/file-extension/oga.gep
Normal file
|
@ -0,0 +1,15 @@
|
|||
[GStreamer Encoding Target]
|
||||
name=oga
|
||||
category=file-extension
|
||||
description=Default target for files with a .ogg and friends extension
|
||||
|
||||
[profile-default]
|
||||
name=default
|
||||
description=Default target for files with a .ogg and friends extension
|
||||
type=container
|
||||
format=audio/ogg
|
||||
|
||||
[streamprofile-default-0]
|
||||
parent=default
|
||||
type=audio
|
||||
format=audio/x-vorbis;audio/x-opus
|
25
data/targets/file-extension/ogv.gep
Normal file
25
data/targets/file-extension/ogv.gep
Normal file
|
@ -0,0 +1,25 @@
|
|||
[GStreamer Encoding Target]
|
||||
name=ogv;ogg
|
||||
category=file-extension
|
||||
description=Default target for files with a .ogg and friends extension
|
||||
|
||||
[profile-default]
|
||||
name=default
|
||||
description=Default target for files with a .ogg and friends extension
|
||||
type=container
|
||||
format=application/ogg
|
||||
|
||||
[streamprofile-default-0]
|
||||
parent=default
|
||||
type=audio
|
||||
format=audio/x-vorbis;audio/x-opus
|
||||
|
||||
[streamprofile-default-1]
|
||||
parent=default
|
||||
type=video
|
||||
format=video/x-theora
|
||||
|
||||
[streamprofile-default-2]
|
||||
parent=default
|
||||
type=video
|
||||
format=video/x-vp8
|
21
data/targets/file-extension/webm.gep
Normal file
21
data/targets/file-extension/webm.gep
Normal file
|
@ -0,0 +1,21 @@
|
|||
[GStreamer Encoding Target]
|
||||
name=webm
|
||||
category=file-extension
|
||||
description=Default target for files with a .webm extension
|
||||
|
||||
[profile-default]
|
||||
name=default
|
||||
description=Default profile for files with a .webm extension. Audio stream can be either vorbis (default) or opus depending on what is available on the system. Video stream will be either in vp8 (default) or vp9.
|
||||
type=container
|
||||
format=video/webm
|
||||
|
||||
[streamprofile-default-0]
|
||||
parent=default
|
||||
type=audio
|
||||
format=audio/x-vorbis;audio/x-opus
|
||||
|
||||
[streamprofile-default-1]
|
||||
parent=default
|
||||
type=video
|
||||
format=video/x-vp8;video/x-vp9
|
||||
pass=0
|
24
data/targets/online-service/youtube.gep
Normal file
24
data/targets/online-service/youtube.gep
Normal file
|
@ -0,0 +1,24 @@
|
|||
[GStreamer Encoding Target]
|
||||
name=youtube;yt
|
||||
category=online-service
|
||||
description=Recommended encoding settings for YouTube
|
||||
|
||||
[profile-default]
|
||||
name=default
|
||||
type=container
|
||||
description=Youtube recommended profile with automatic audio setting
|
||||
format=video/quicktime
|
||||
preset=Profile YouTube
|
||||
|
||||
[streamprofile-default-0]
|
||||
parent=default
|
||||
type=audio
|
||||
format=audio/mpeg,mpegversion=4,base-profile=lc
|
||||
restriction=audio/x-raw,channels=6,rate={48000,96000};audio/x-raw,channels=2,rate={48000,96000}
|
||||
|
||||
[streamprofile-default-1]
|
||||
parent=default
|
||||
type=video
|
||||
format=video/x-h264,profile=high
|
||||
preset=Profile YouTube
|
||||
pass=0
|
File diff suppressed because it is too large
Load diff
8
docs/libs/transcoder/index.md
Normal file
8
docs/libs/transcoder/index.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# GstTranscoder
|
||||
|
||||
High level API to transcode streams
|
||||
|
||||
This library should be linked to by getting cflags and libs from
|
||||
gstreamer-transcoder-{{ gst_api_version.md }}
|
||||
|
||||
> NOTE: This library API is considered *unstable*
|
1
docs/libs/transcoder/sitemap.txt
Normal file
1
docs/libs/transcoder/sitemap.txt
Normal file
|
@ -0,0 +1 @@
|
|||
gi-index
|
|
@ -103,6 +103,7 @@ if build_gir
|
|||
{'name': 'adaptivedemux', 'lib': gstadaptivedemux_dep},
|
||||
{'name': 'webrtc', 'gir': webrtc_gir, 'lib': gstwebrtc_dep, 'suffix': 'lib'},
|
||||
{'name': 'audio', 'gir': audio_gir, 'lib': gstbadaudio_dep, 'prefix': 'bad-'},
|
||||
{'name': 'transcoder', 'gir': transcoder_gir, 'lib': gst_transcoder_dep},
|
||||
]
|
||||
endif
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ subdir('mpegts')
|
|||
subdir('opencv')
|
||||
subdir('player')
|
||||
subdir('sctp')
|
||||
subdir('transcoder')
|
||||
subdir('vulkan')
|
||||
subdir('wayland')
|
||||
subdir('webrtc')
|
||||
|
|
1609
gst-libs/gst/transcoder/gsttranscoder.c
Normal file
1609
gst-libs/gst/transcoder/gsttranscoder.c
Normal file
File diff suppressed because it is too large
Load diff
141
gst-libs/gst/transcoder/gsttranscoder.h
Normal file
141
gst-libs/gst/transcoder/gsttranscoder.h
Normal file
|
@ -0,0 +1,141 @@
|
|||
#ifndef __GST_TRANSCODER_H
|
||||
#define __GST_TRANSCODER_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
#include "transcoder-prelude.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstTranscoderSignalDispatcher GstTranscoderSignalDispatcher;
|
||||
typedef struct _GstTranscoderSignalDispatcherInterface GstTranscoderSignalDispatcherInterface;
|
||||
|
||||
/*********** Error definitions ************/
|
||||
#define GST_TRANSCODER_ERROR (gst_transcoder_error_quark ())
|
||||
#define GST_TYPE_TRANSCODER_ERROR (gst_transcoder_error_get_type ())
|
||||
|
||||
/**
|
||||
* GstTranscoderError:
|
||||
* @GST_TRANSCODER_ERROR_FAILED: generic error.
|
||||
*/
|
||||
typedef enum {
|
||||
GST_TRANSCODER_ERROR_FAILED = 0
|
||||
} GstTranscoderError;
|
||||
|
||||
GST_TRANSCODER_API
|
||||
GQuark gst_transcoder_error_quark (void);
|
||||
GST_TRANSCODER_API
|
||||
GType gst_transcoder_error_get_type (void);
|
||||
GST_TRANSCODER_API
|
||||
const gchar * gst_transcoder_error_get_name (GstTranscoderError error);
|
||||
|
||||
/*********** GstTranscoder definition ************/
|
||||
#define GST_TYPE_TRANSCODER (gst_transcoder_get_type ())
|
||||
#define GST_TRANSCODER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TRANSCODER, GstTranscoder))
|
||||
#define GST_TRANSCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TRANSCODER, GstTranscoderClass))
|
||||
#define GST_IS_TRANSCODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TRANSCODER))
|
||||
#define GST_IS_TRANSCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TRANSCODER))
|
||||
#define GST_TRANSCODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TRANSCODER, GstTranscoderClass))
|
||||
|
||||
typedef struct _GstTranscoder GstTranscoder;
|
||||
typedef struct _GstTranscoderClass GstTranscoderClass;
|
||||
typedef struct _GstTranscoderPrivate GstTranscoderPrivate;
|
||||
|
||||
GST_TRANSCODER_API
|
||||
GType gst_transcoder_get_type (void);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
GstTranscoder * gst_transcoder_new (const gchar * source_uri,
|
||||
const gchar * dest_uri,
|
||||
const gchar * encoding_profile);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
GstTranscoder * gst_transcoder_new_full (const gchar * source_uri,
|
||||
const gchar * dest_uri,
|
||||
GstEncodingProfile *profile,
|
||||
GstTranscoderSignalDispatcher *signal_dispatcher);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
gboolean gst_transcoder_run (GstTranscoder *self,
|
||||
GError ** error);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
void gst_transcoder_set_cpu_usage (GstTranscoder *self,
|
||||
gint cpu_usage);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
void gst_transcoder_run_async (GstTranscoder *self);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
void gst_transcoder_set_position_update_interval (GstTranscoder *self,
|
||||
guint interval);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
gchar * gst_transcoder_get_source_uri (GstTranscoder * self);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
gchar * gst_transcoder_get_dest_uri (GstTranscoder * self);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
guint gst_transcoder_get_position_update_interval (GstTranscoder *self);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
GstClockTime gst_transcoder_get_position (GstTranscoder * self);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
GstClockTime gst_transcoder_get_duration (GstTranscoder * self);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
GstElement * gst_transcoder_get_pipeline (GstTranscoder * self);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
gboolean gst_transcoder_get_avoid_reencoding (GstTranscoder * self);
|
||||
GST_TRANSCODER_API
|
||||
void gst_transcoder_set_avoid_reencoding (GstTranscoder * self,
|
||||
gboolean avoid_reencoding);
|
||||
|
||||
|
||||
/****************** Signal dispatcher *******************************/
|
||||
|
||||
#define GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER (gst_transcoder_signal_dispatcher_get_type ())
|
||||
#define GST_TRANSCODER_SIGNAL_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER, GstTranscoderSignalDispatcher))
|
||||
#define GST_IS_TRANSCODER_SIGNAL_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER))
|
||||
#define GST_TRANSCODER_SIGNAL_DISPATCHER_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER, GstTranscoderSignalDispatcherInterface))
|
||||
|
||||
struct _GstTranscoderSignalDispatcherInterface {
|
||||
GTypeInterface parent_iface;
|
||||
|
||||
void (*dispatch) (GstTranscoderSignalDispatcher * self,
|
||||
GstTranscoder * transcoder,
|
||||
void (*emitter) (gpointer data),
|
||||
gpointer data,
|
||||
GDestroyNotify destroy);
|
||||
};
|
||||
|
||||
typedef struct _GstTranscoderGMainContextSignalDispatcher GstTranscoderGMainContextSignalDispatcher;
|
||||
typedef struct _GstTranscoderGMainContextSignalDispatcherClass GstTranscoderGMainContextSignalDispatcherClass;
|
||||
|
||||
GST_TRANSCODER_API
|
||||
GType gst_transcoder_signal_dispatcher_get_type (void);
|
||||
|
||||
#define GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (gst_transcoder_g_main_context_signal_dispatcher_get_type ())
|
||||
#define GST_IS_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER))
|
||||
#define GST_IS_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER))
|
||||
#define GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER, GstTranscoderGMainContextSignalDispatcherClass))
|
||||
#define GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER, GstTranscoderGMainContextSignalDispatcher))
|
||||
#define GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER, GstTranscoderGMainContextSignalDispatcherClass))
|
||||
#define GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_CAST(obj) ((GstTranscoderGMainContextSignalDispatcher*)(obj))
|
||||
|
||||
GST_TRANSCODER_API
|
||||
GType gst_transcoder_g_main_context_signal_dispatcher_get_type (void);
|
||||
|
||||
GST_TRANSCODER_API
|
||||
GstTranscoderSignalDispatcher * gst_transcoder_g_main_context_signal_dispatcher_new (GMainContext * application_context);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
33
gst-libs/gst/transcoder/meson.build
Normal file
33
gst-libs/gst/transcoder/meson.build
Normal file
|
@ -0,0 +1,33 @@
|
|||
sources = files(['gsttranscoder.c'])
|
||||
headers = files(['gsttranscoder.h', 'transcoder-prelude.h'])
|
||||
|
||||
install_headers(headers, subdir : 'gstreamer-' + api_version + '/gst/transcoder')
|
||||
|
||||
gst_transcoder = library('gsttranscoder-' + api_version,
|
||||
sources,
|
||||
install: true,
|
||||
include_directories : [configinc, libsinc],
|
||||
dependencies: [gst_dep, gstpbutils_dep],
|
||||
c_args: gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API', '-DBUILDING_GST_TRANSCODER'],
|
||||
soversion : soversion,
|
||||
)
|
||||
if build_gir
|
||||
transcoder_gir = gnome.generate_gir(gst_transcoder,
|
||||
sources : sources + headers,
|
||||
nsversion : api_version,
|
||||
namespace : 'GstTranscoder',
|
||||
identifier_prefix : 'Gst',
|
||||
symbol_prefix : 'gst_',
|
||||
includes : ['GObject-2.0',
|
||||
'Gst-' + api_version,
|
||||
'GstPbutils-' + api_version],
|
||||
dependencies: [gst_dep, gstpbutils_dep],
|
||||
install : true,
|
||||
extra_args : ['--add-init-section=extern gboolean gst_init(gint *argc, gchar **argv); gst_init(NULL,NULL);']
|
||||
)
|
||||
endif
|
||||
|
||||
gst_transcoder_dep = declare_dependency(link_with: gst_transcoder,
|
||||
dependencies : [gst_dep, gstpbutils_dep],
|
||||
include_directories : [libsinc]
|
||||
)
|
36
gst-libs/gst/transcoder/transcoder-prelude.h
Normal file
36
gst-libs/gst/transcoder/transcoder-prelude.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* GStreamer Transcoder Library
|
||||
*
|
||||
* Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com>
|
||||
*
|
||||
* transcoder-prelude.h: prelude include header for the gst-transcoder library
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_TRANSCODER_PRELUDE_H__
|
||||
#define __GST_TRANSCODER_PRELUDE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#ifndef GST_TRANSCODER_API
|
||||
# ifdef BUILDING_GST_TRANSCODER
|
||||
# define GST_TRANSCODER_API GST_API_EXPORT /* from config.h */
|
||||
# else
|
||||
# define GST_TRANSCODER_API GST_API_IMPORT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* __GST_TRANSCODER_PRELUDE_H__ */
|
|
@ -9,7 +9,7 @@ foreach plugin : ['accurip', 'adpcmdec', 'adpcmenc', 'aiff', 'asfmux',
|
|||
'midi', 'mpegdemux', 'mpegpsmux', 'mpegtsdemux', 'mpegtsmux',
|
||||
'mxf', 'netsim', 'onvif', 'pcapparse', 'pnm', 'proxy',
|
||||
'rawparse', 'removesilence', 'rist', 'rtp', 'sdp', 'segmentclip',
|
||||
'siren', 'smooth', 'speed', 'subenc', 'timecode',
|
||||
'siren', 'smooth', 'speed', 'subenc', 'timecode', 'transcode',
|
||||
'videofilters', 'videoframe_audiolevel', 'videoparsers',
|
||||
'videosignal', 'vmnc', 'y4m', 'yadif']
|
||||
if not get_option(plugin).disabled()
|
||||
|
|
220
gst/transcode/gst-cpu-throttling-clock.c
Normal file
220
gst/transcode/gst-cpu-throttling-clock.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETRUSAGE
|
||||
#include "gst-cpu-throttling-clock.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "gst-cpu-throttling-clock.h"
|
||||
|
||||
/**
|
||||
* SECTION: gst-cpu-throttling-clock
|
||||
* @title: GstCpuThrottlingClock
|
||||
* @short_description: TODO
|
||||
*
|
||||
* TODO
|
||||
*/
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_cpu_throttling_clock_debug);
|
||||
#define GST_CAT_DEFAULT gst_cpu_throttling_clock_debug
|
||||
|
||||
struct _GstCpuThrottlingClockPrivate
|
||||
{
|
||||
guint wanted_cpu_usage;
|
||||
|
||||
GstClock *sclock;
|
||||
GstClockTime current_wait_time;
|
||||
GstPoll *timer;
|
||||
struct rusage last_usage;
|
||||
|
||||
GstClockID evaluate_wait_time;
|
||||
GstClockTime time_between_evals;
|
||||
};
|
||||
|
||||
#define parent_class gst_cpu_throttling_clock_parent_class
|
||||
G_DEFINE_TYPE_WITH_CODE (GstCpuThrottlingClock, gst_cpu_throttling_clock, GST_TYPE_CLOCK, G_ADD_PRIVATE(GstCpuThrottlingClock))
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_FIRST,
|
||||
PROP_CPU_USAGE,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
|
||||
/* *INDENT-ON* */
|
||||
|
||||
static void
|
||||
gst_cpu_throttling_clock_get_property (GObject * object,
|
||||
guint property_id, GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstCpuThrottlingClock *self = GST_CPU_THROTTLING_CLOCK (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_CPU_USAGE:
|
||||
g_value_set_uint (value, self->priv->wanted_cpu_usage);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cpu_throttling_clock_set_property (GObject * object,
|
||||
guint property_id, const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstCpuThrottlingClock *self = GST_CPU_THROTTLING_CLOCK (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_CPU_USAGE:
|
||||
self->priv->wanted_cpu_usage = g_value_get_uint (value);
|
||||
if (self->priv->wanted_cpu_usage == 0)
|
||||
self->priv->wanted_cpu_usage = 100;
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_transcoder_adjust_wait_time (GstClock * sync_clock, GstClockTime time,
|
||||
GstClockID id, GstCpuThrottlingClock * self)
|
||||
{
|
||||
struct rusage ru;
|
||||
float delta_usage, usage, coef;
|
||||
|
||||
GstCpuThrottlingClockPrivate *priv = self->priv;
|
||||
|
||||
getrusage (RUSAGE_SELF, &ru);
|
||||
delta_usage = GST_TIMEVAL_TO_TIME (ru.ru_utime) -
|
||||
GST_TIMEVAL_TO_TIME (self->priv->last_usage.ru_utime);
|
||||
usage =
|
||||
((float) delta_usage / self->priv->time_between_evals * 100) /
|
||||
g_get_num_processors ();
|
||||
|
||||
self->priv->last_usage = ru;
|
||||
|
||||
coef = GST_MSECOND / 10;
|
||||
if (usage < (gfloat) priv->wanted_cpu_usage) {
|
||||
coef = -coef;
|
||||
}
|
||||
|
||||
priv->current_wait_time = CLAMP (0,
|
||||
(GstClockTime) priv->current_wait_time + coef, GST_SECOND);
|
||||
|
||||
GST_DEBUG_OBJECT (self,
|
||||
"Avg is %f (wanted %d) => %" GST_TIME_FORMAT, usage,
|
||||
self->priv->wanted_cpu_usage, GST_TIME_ARGS (priv->current_wait_time));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstClockReturn
|
||||
_wait (GstClock * clock, GstClockEntry * entry, GstClockTimeDiff * jitter)
|
||||
{
|
||||
GstCpuThrottlingClock *self = GST_CPU_THROTTLING_CLOCK (clock);
|
||||
|
||||
if (!self->priv->evaluate_wait_time) {
|
||||
if (!(self->priv->sclock)) {
|
||||
GST_ERROR_OBJECT (clock, "Could not find any system clock"
|
||||
" to start the wait time evaluation task");
|
||||
} else {
|
||||
self->priv->evaluate_wait_time =
|
||||
gst_clock_new_periodic_id (self->priv->sclock,
|
||||
gst_clock_get_time (self->priv->sclock),
|
||||
self->priv->time_between_evals);
|
||||
|
||||
gst_clock_id_wait_async (self->priv->evaluate_wait_time,
|
||||
(GstClockCallback) gst_transcoder_adjust_wait_time,
|
||||
(gpointer) self, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (GST_CLOCK_ENTRY_STATUS (entry) == GST_CLOCK_UNSCHEDULED))
|
||||
return GST_CLOCK_UNSCHEDULED;
|
||||
|
||||
if (gst_poll_wait (self->priv->timer, self->priv->current_wait_time)) {
|
||||
GST_INFO_OBJECT (self, "Something happened on the poll");
|
||||
}
|
||||
|
||||
return GST_CLOCK_ENTRY_STATUS (entry);
|
||||
}
|
||||
|
||||
static GstClockTime
|
||||
_get_internal_time (GstClock * clock)
|
||||
{
|
||||
GstCpuThrottlingClock *self = GST_CPU_THROTTLING_CLOCK (clock);
|
||||
|
||||
return gst_clock_get_internal_time (self->priv->sclock);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cpu_throttling_clock_dispose (GObject * object)
|
||||
{
|
||||
GstCpuThrottlingClock *self = GST_CPU_THROTTLING_CLOCK (object);
|
||||
|
||||
if (self->priv->evaluate_wait_time) {
|
||||
gst_clock_id_unschedule (self->priv->evaluate_wait_time);
|
||||
gst_clock_id_unref (self->priv->evaluate_wait_time);
|
||||
self->priv->evaluate_wait_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cpu_throttling_clock_class_init (GstCpuThrottlingClockClass * klass)
|
||||
{
|
||||
GObjectClass *oclass = G_OBJECT_CLASS (klass);
|
||||
GstClockClass *clock_klass = GST_CLOCK_CLASS (klass);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_cpu_throttling_clock_debug, "cpuclock", 0,
|
||||
"UriTranscodebin element");
|
||||
|
||||
oclass->get_property = gst_cpu_throttling_clock_get_property;
|
||||
oclass->set_property = gst_cpu_throttling_clock_set_property;
|
||||
oclass->dispose = gst_cpu_throttling_clock_dispose;
|
||||
|
||||
/**
|
||||
* GstCpuThrottlingClock:cpu-usage:
|
||||
*
|
||||
* Since: UNRELEASED
|
||||
*/
|
||||
param_specs[PROP_CPU_USAGE] = g_param_spec_uint ("cpu-usage", "cpu-usage",
|
||||
"The percentage of CPU to try to use with the processus running the "
|
||||
"pipeline driven by the clock", 0, 100,
|
||||
100, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (oclass, PROP_LAST, param_specs);
|
||||
|
||||
clock_klass->wait = GST_DEBUG_FUNCPTR (_wait);
|
||||
clock_klass->get_internal_time = _get_internal_time;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_cpu_throttling_clock_init (GstCpuThrottlingClock * self)
|
||||
{
|
||||
self->priv = gst_cpu_throttling_clock_get_instance_private (self);
|
||||
|
||||
self->priv->current_wait_time = GST_MSECOND;
|
||||
self->priv->wanted_cpu_usage = 100;
|
||||
self->priv->timer = gst_poll_new_timer ();
|
||||
self->priv->time_between_evals = GST_SECOND / 4;
|
||||
self->priv->sclock = GST_CLOCK (gst_system_clock_obtain ());
|
||||
|
||||
|
||||
getrusage (RUSAGE_SELF, &self->priv->last_usage);
|
||||
}
|
||||
|
||||
GstCpuThrottlingClock *
|
||||
gst_cpu_throttling_clock_new (guint cpu_usage)
|
||||
{
|
||||
return g_object_new (GST_TYPE_CPU_THROTTLING_CLOCK, "cpu-usage",
|
||||
cpu_usage, NULL);
|
||||
}
|
||||
#endif
|
60
gst/transcode/gst-cpu-throttling-clock.h
Normal file
60
gst/transcode/gst-cpu-throttling-clock.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* gst-cpu-throttling-clock.h
|
||||
*
|
||||
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef __GST_CPU_THROTTLING_CLOCK_H__
|
||||
#define __GST_CPU_THROTTLING_CLOCK_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstCpuThrottlingClock GstCpuThrottlingClock;
|
||||
typedef struct _GstCpuThrottlingClockClass GstCpuThrottlingClockClass;
|
||||
typedef struct _GstCpuThrottlingClockPrivate GstCpuThrottlingClockPrivate;
|
||||
|
||||
GType gst_cpu_throttling_clock_get_type (void) G_GNUC_CONST;
|
||||
|
||||
#define GST_TYPE_CPU_THROTTLING_CLOCK (gst_cpu_throttling_clock_get_type ())
|
||||
#define GST_CPU_THROTTLING_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_CPU_THROTTLING_CLOCK, GstCpuThrottlingClock))
|
||||
#define GST_CPU_THROTTLING_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_CPU_THROTTLING_CLOCK, GstCpuThrottlingClockClass))
|
||||
#define GST_IS_CPU_THROTTLING_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_CPU_THROTTLING_CLOCK))
|
||||
#define GST_IS_CPU_THROTTLING_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_CPU_THROTTLING_CLOCK))
|
||||
#define GST_CPU_THROTTLING_CLOCK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CPU_THROTTLING_CLOCK, GstCpuThrottlingClockClass))
|
||||
|
||||
struct _GstCpuThrottlingClockClass
|
||||
{
|
||||
/*<private>*/
|
||||
GstClockClass parent_class;
|
||||
};
|
||||
|
||||
struct _GstCpuThrottlingClock
|
||||
{
|
||||
/*<private>*/
|
||||
GstClock parent;
|
||||
GstCpuThrottlingClockPrivate *priv;
|
||||
};
|
||||
|
||||
GstCpuThrottlingClock * gst_cpu_throttling_clock_new (guint cpu_usage);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* #ifndef __GST_CPU_THROTTLING_CLOCK_H__*/
|
614
gst/transcode/gsttranscodebin.c
Normal file
614
gst/transcode/gsttranscodebin.c
Normal file
|
@ -0,0 +1,614 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com>
|
||||
*
|
||||
* gsttranscodebin.c:
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gsttranscoding.h"
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
#include <gst/pbutils/missing-plugins.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_transcodebin_debug);
|
||||
#define GST_CAT_DEFAULT gst_transcodebin_debug
|
||||
|
||||
static GstStaticPadTemplate transcode_bin_sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
static GstStaticPadTemplate transcode_bin_src_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstBin parent;
|
||||
|
||||
GstElement *decodebin;
|
||||
GstElement *encodebin;
|
||||
|
||||
GstEncodingProfile *profile;
|
||||
gboolean avoid_reencoding;
|
||||
GstPad *sinkpad;
|
||||
GstPad *srcpad;
|
||||
|
||||
GstElement *audio_filter;
|
||||
GstElement *video_filter;
|
||||
} GstTranscodeBin;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstBinClass parent;
|
||||
|
||||
} GstTranscodeBinClass;
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#define GST_TYPE_TRANSCODE_BIN (gst_transcode_bin_get_type ())
|
||||
#define GST_TRANSCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TRANSCODE_BIN, GstTranscodeBin))
|
||||
#define GST_TRANSCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TRANSCODE_BIN_TYPE, GstTranscodeBinClass))
|
||||
#define GST_IS_TRANSCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TRANSCODE_BIN_TYPE))
|
||||
#define GST_IS_TRANSCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TRANSCODE_BIN_TYPE))
|
||||
#define GST_TRANSCODE_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TRANSCODE_BIN_TYPE, GstTranscodeBinClass))
|
||||
|
||||
#define DEFAULT_AVOID_REENCODING FALSE
|
||||
|
||||
G_DEFINE_TYPE (GstTranscodeBin, gst_transcode_bin, GST_TYPE_BIN)
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_PROFILE,
|
||||
PROP_AVOID_REENCODING,
|
||||
PROP_VIDEO_FILTER,
|
||||
PROP_AUDIO_FILTER,
|
||||
LAST_PROP
|
||||
};
|
||||
|
||||
static void
|
||||
post_missing_plugin_error (GstElement * dec, const gchar * element_name)
|
||||
{
|
||||
GstMessage *msg;
|
||||
|
||||
msg = gst_missing_element_message_new (dec, element_name);
|
||||
gst_element_post_message (dec, msg);
|
||||
|
||||
GST_ELEMENT_ERROR (dec, CORE, MISSING_PLUGIN,
|
||||
("Missing element '%s' - check your GStreamer installation.",
|
||||
element_name), (NULL));
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
|
||||
static GstPad *
|
||||
_insert_filter (GstTranscodeBin * self, GstPad * sinkpad, GstPad * pad,
|
||||
GstCaps * caps)
|
||||
{
|
||||
GstPad *filter_src = NULL, *filter_sink = NULL;
|
||||
GstElement *filter = NULL;
|
||||
GstObject *filter_parent;
|
||||
|
||||
if (self->video_filter &&
|
||||
!g_strcmp0 (gst_structure_get_name (gst_caps_get_structure (caps, 0)),
|
||||
"video/x-raw")) {
|
||||
filter = self->video_filter;
|
||||
} else if (self->audio_filter &&
|
||||
!g_strcmp0 (gst_structure_get_name (gst_caps_get_structure (caps, 0)),
|
||||
"audio/x-raw")) {
|
||||
filter = self->audio_filter;
|
||||
}
|
||||
|
||||
if (!filter)
|
||||
return pad;
|
||||
|
||||
if ((filter_parent = gst_object_get_parent (GST_OBJECT (filter)))) {
|
||||
GST_WARNING_OBJECT (self,
|
||||
"Filter already in use (inside %" GST_PTR_FORMAT ").", filter_parent);
|
||||
GST_FIXME_OBJECT (self,
|
||||
"Handle transcoding several streams of a same kind.");
|
||||
gst_object_unref (filter_parent);
|
||||
|
||||
return pad;
|
||||
}
|
||||
|
||||
/* We are guaranteed filters only have 1 unique sinkpad and srcpad */
|
||||
GST_OBJECT_LOCK (filter);
|
||||
filter_sink = filter->sinkpads->data;
|
||||
filter_src = filter->srcpads->data;
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
|
||||
gst_bin_add (GST_BIN (self), gst_object_ref (filter));
|
||||
if (G_UNLIKELY (gst_pad_link (pad, filter_sink) != GST_PAD_LINK_OK)) {
|
||||
GstCaps *othercaps = gst_pad_get_current_caps (sinkpad);
|
||||
caps = gst_pad_get_current_caps (pad);
|
||||
|
||||
GST_ELEMENT_ERROR (self, CORE, PAD,
|
||||
(NULL),
|
||||
("Couldn't link pads \n\n%" GST_PTR_FORMAT "\n\n and \n\n %"
|
||||
GST_PTR_FORMAT "\n\n", caps, othercaps));
|
||||
|
||||
gst_caps_unref (caps);
|
||||
gst_caps_unref (othercaps);
|
||||
}
|
||||
|
||||
gst_element_sync_state_with_parent (filter);
|
||||
|
||||
return filter_src;
|
||||
}
|
||||
|
||||
static void
|
||||
pad_added_cb (GstElement * decodebin, GstPad * pad, GstTranscodeBin * self)
|
||||
{
|
||||
GstCaps *caps;
|
||||
GstPad *sinkpad = NULL;
|
||||
GstPadLinkReturn lret;
|
||||
|
||||
caps = gst_pad_query_caps (pad, NULL);
|
||||
|
||||
GST_DEBUG_OBJECT (decodebin, "Pad added, caps: %" GST_PTR_FORMAT, caps);
|
||||
|
||||
g_signal_emit_by_name (self->encodebin, "request-pad", caps, &sinkpad);
|
||||
|
||||
if (sinkpad == NULL) {
|
||||
gchar *stream_id = gst_pad_get_stream_id (pad);
|
||||
|
||||
GST_ELEMENT_WARNING_WITH_DETAILS (self, STREAM, FORMAT,
|
||||
(NULL), ("Stream with caps: %" GST_PTR_FORMAT " can not be"
|
||||
" encoded in the defined encoding formats",
|
||||
caps),
|
||||
("can-t-encode-stream", G_TYPE_BOOLEAN, TRUE,
|
||||
"stream-caps", GST_TYPE_CAPS, caps,
|
||||
"stream-id", G_TYPE_STRING, stream_id, NULL));
|
||||
|
||||
g_free (stream_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (caps)
|
||||
gst_caps_unref (caps);
|
||||
|
||||
pad = _insert_filter (self, sinkpad, pad, caps);
|
||||
lret = gst_pad_link (pad, sinkpad);
|
||||
switch (lret) {
|
||||
case GST_PAD_LINK_OK:
|
||||
break;
|
||||
case GST_PAD_LINK_WAS_LINKED:
|
||||
GST_FIXME_OBJECT (self, "Pad %" GST_PTR_FORMAT " was already linked",
|
||||
sinkpad);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
GstCaps *othercaps = gst_pad_query_caps (sinkpad, NULL);
|
||||
caps = gst_pad_get_current_caps (pad);
|
||||
|
||||
GST_ELEMENT_ERROR_WITH_DETAILS (self, CORE, PAD,
|
||||
(NULL),
|
||||
("Couldn't link pads:\n %" GST_PTR_FORMAT ": %" GST_PTR_FORMAT
|
||||
"\nand:\n"
|
||||
" %" GST_PTR_FORMAT ": %" GST_PTR_FORMAT "\n\n",
|
||||
pad, caps, sinkpad, othercaps),
|
||||
("linking-error", GST_TYPE_PAD_LINK_RETURN, lret,
|
||||
"source-pad", GST_TYPE_PAD, pad,
|
||||
"source-caps", GST_TYPE_CAPS, caps,
|
||||
"sink-pad", GST_TYPE_PAD, sinkpad,
|
||||
"sink-caps", GST_TYPE_CAPS, othercaps, NULL));
|
||||
|
||||
gst_caps_unref (caps);
|
||||
if (othercaps)
|
||||
gst_caps_unref (othercaps);
|
||||
}
|
||||
}
|
||||
|
||||
gst_object_unref (sinkpad);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
make_encodebin (GstTranscodeBin * self)
|
||||
{
|
||||
GstPad *pad;
|
||||
GST_INFO_OBJECT (self, "making new encodebin");
|
||||
|
||||
if (!self->profile)
|
||||
goto no_profile;
|
||||
|
||||
self->encodebin = gst_element_factory_make ("encodebin", NULL);
|
||||
if (!self->encodebin)
|
||||
goto no_encodebin;
|
||||
|
||||
gst_bin_add (GST_BIN (self), self->encodebin);
|
||||
g_object_set (self->encodebin, "profile", self->profile, NULL);
|
||||
|
||||
pad = gst_element_get_static_pad (self->encodebin, "src");
|
||||
if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad)) {
|
||||
|
||||
gst_object_unref (pad);
|
||||
GST_ERROR_OBJECT (self, "Could not ghost %" GST_PTR_FORMAT " srcpad",
|
||||
self->encodebin);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
gst_object_unref (pad);
|
||||
|
||||
return gst_element_sync_state_with_parent (self->encodebin);
|
||||
|
||||
/* ERRORS */
|
||||
no_encodebin:
|
||||
{
|
||||
post_missing_plugin_error (GST_ELEMENT_CAST (self), "encodebin");
|
||||
|
||||
GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
|
||||
("No encodebin element, check your installation"));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
/* ERRORS */
|
||||
no_profile:
|
||||
{
|
||||
GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
|
||||
("No GstEncodingProfile set, can not run."));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
make_decodebin (GstTranscodeBin * self)
|
||||
{
|
||||
GstPad *pad;
|
||||
GST_INFO_OBJECT (self, "making new decodebin");
|
||||
|
||||
self->decodebin = gst_element_factory_make ("decodebin", NULL);
|
||||
|
||||
if (!self->decodebin)
|
||||
goto no_decodebin;
|
||||
|
||||
if (self->avoid_reencoding) {
|
||||
GstCaps *decodecaps;
|
||||
|
||||
g_object_get (self->decodebin, "caps", &decodecaps, NULL);
|
||||
if (GST_IS_ENCODING_CONTAINER_PROFILE (self->profile)) {
|
||||
GList *tmp;
|
||||
|
||||
decodecaps = gst_caps_make_writable (decodecaps);
|
||||
for (tmp = (GList *)
|
||||
gst_encoding_container_profile_get_profiles
|
||||
(GST_ENCODING_CONTAINER_PROFILE (self->profile)); tmp;
|
||||
tmp = tmp->next) {
|
||||
GstCaps *encodecaps = gst_encoding_profile_get_format (tmp->data);
|
||||
GstCaps *restrictions =
|
||||
gst_encoding_profile_get_restriction (tmp->data);
|
||||
|
||||
if (!restrictions)
|
||||
gst_caps_append (decodecaps, encodecaps);
|
||||
else
|
||||
gst_caps_unref (restrictions);
|
||||
}
|
||||
}
|
||||
g_object_set (self->decodebin, "caps", decodecaps, NULL);
|
||||
gst_caps_unref (decodecaps);
|
||||
}
|
||||
|
||||
g_signal_connect (self->decodebin, "pad-added", G_CALLBACK (pad_added_cb),
|
||||
self);
|
||||
|
||||
gst_bin_add (GST_BIN (self), self->decodebin);
|
||||
pad = gst_element_get_static_pad (self->decodebin, "sink");
|
||||
if (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad)) {
|
||||
|
||||
gst_object_unref (pad);
|
||||
GST_ERROR_OBJECT (self, "Could not ghost %" GST_PTR_FORMAT " sinkpad",
|
||||
self->decodebin);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_object_unref (pad);
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
no_decodebin:
|
||||
{
|
||||
post_missing_plugin_error (GST_ELEMENT_CAST (self), "decodebin");
|
||||
GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
|
||||
("No decodebin element, check your installation"));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
remove_all_children (GstTranscodeBin * self)
|
||||
{
|
||||
if (self->encodebin) {
|
||||
gst_element_set_state (self->encodebin, GST_STATE_NULL);
|
||||
gst_bin_remove (GST_BIN (self), self->encodebin);
|
||||
self->encodebin = NULL;
|
||||
}
|
||||
|
||||
if (self->video_filter && GST_OBJECT_PARENT (self->video_filter)) {
|
||||
gst_element_set_state (self->video_filter, GST_STATE_NULL);
|
||||
gst_bin_remove (GST_BIN (self), self->video_filter);
|
||||
}
|
||||
|
||||
if (self->audio_filter && GST_OBJECT_PARENT (self->audio_filter)) {
|
||||
gst_element_set_state (self->audio_filter, GST_STATE_NULL);
|
||||
gst_bin_remove (GST_BIN (self), self->audio_filter);
|
||||
}
|
||||
|
||||
if (self->decodebin) {
|
||||
gst_element_set_state (self->decodebin, GST_STATE_NULL);
|
||||
gst_bin_remove (GST_BIN (self), self->decodebin);
|
||||
self->decodebin = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_transcode_bin_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
GstTranscodeBin *self = GST_TRANSCODE_BIN (element);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||
|
||||
if (!make_encodebin (self))
|
||||
goto setup_failed;
|
||||
|
||||
if (!make_decodebin (self))
|
||||
goto setup_failed;
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret =
|
||||
GST_ELEMENT_CLASS (gst_transcode_bin_parent_class)->change_state (element,
|
||||
transition);
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
goto beach;
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
remove_all_children (self);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
beach:
|
||||
return ret;
|
||||
|
||||
setup_failed:
|
||||
remove_all_children (self);
|
||||
return GST_STATE_CHANGE_FAILURE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_transcode_bin_dispose (GObject * object)
|
||||
{
|
||||
GstTranscodeBin *self = (GstTranscodeBin *) object;
|
||||
|
||||
g_clear_object (&self->video_filter);
|
||||
g_clear_object (&self->audio_filter);
|
||||
|
||||
G_OBJECT_CLASS (gst_transcode_bin_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_transcode_bin_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstTranscodeBin *self = GST_TRANSCODE_BIN (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PROFILE:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_object (value, self->profile);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_AVOID_REENCODING:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_boolean (value, self->avoid_reencoding);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_AUDIO_FILTER:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_object (value, self->audio_filter);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_VIDEO_FILTER:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_object (value, self->video_filter);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_set_filter (GstTranscodeBin * self, GstElement * filter, GstElement ** mfilter)
|
||||
{
|
||||
if (filter) {
|
||||
GST_OBJECT_LOCK (filter);
|
||||
if (filter->numsinkpads != 1) {
|
||||
GST_ERROR_OBJECT (self, "Can not use %" GST_PTR_FORMAT
|
||||
" as filter as it does not have "
|
||||
" one and only one sinkpad", filter);
|
||||
goto bail_out;
|
||||
} else if (filter->numsrcpads != 1) {
|
||||
GST_ERROR_OBJECT (self, "Can not use %" GST_PTR_FORMAT
|
||||
" as filter as it does not have " " one and only one srcpad", filter);
|
||||
goto bail_out;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
}
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
*mfilter = filter;
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return;
|
||||
|
||||
bail_out:
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_transcode_bin_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstTranscodeBin *self = GST_TRANSCODE_BIN (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PROFILE:
|
||||
GST_OBJECT_LOCK (self);
|
||||
self->profile = g_value_dup_object (value);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_AVOID_REENCODING:
|
||||
GST_OBJECT_LOCK (self);
|
||||
self->avoid_reencoding = g_value_get_boolean (value);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_AUDIO_FILTER:
|
||||
_set_filter (self, g_value_dup_object (value), &self->audio_filter);
|
||||
break;
|
||||
case PROP_VIDEO_FILTER:
|
||||
_set_filter (self, g_value_dup_object (value), &self->video_filter);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_transcode_bin_class_init (GstTranscodeBinClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *gstelement_klass;
|
||||
|
||||
object_class->dispose = gst_transcode_bin_dispose;
|
||||
object_class->get_property = gst_transcode_bin_get_property;
|
||||
object_class->set_property = gst_transcode_bin_set_property;
|
||||
|
||||
gstelement_klass = (GstElementClass *) klass;
|
||||
gstelement_klass->change_state =
|
||||
GST_DEBUG_FUNCPTR (gst_transcode_bin_change_state);
|
||||
|
||||
gst_element_class_add_pad_template (gstelement_klass,
|
||||
gst_static_pad_template_get (&transcode_bin_sink_template));
|
||||
gst_element_class_add_pad_template (gstelement_klass,
|
||||
gst_static_pad_template_get (&transcode_bin_src_template));
|
||||
|
||||
gst_element_class_set_static_metadata (gstelement_klass,
|
||||
"Transcode Bin", "Generic/Bin/Encoding",
|
||||
"Autoplug and transcoder a stream",
|
||||
"Thibault Saunier <tsaunier@igalia.com>");
|
||||
|
||||
/**
|
||||
* GstTranscodeBin:profile:
|
||||
*
|
||||
* The #GstEncodingProfile to use. This property must be set before going
|
||||
* to %GST_STATE_PAUSED or higher.
|
||||
*/
|
||||
g_object_class_install_property (object_class, PROP_PROFILE,
|
||||
g_param_spec_object ("profile", "Profile",
|
||||
"The GstEncodingProfile to use", GST_TYPE_ENCODING_PROFILE,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GstTranscodeBin:avoid-reencoding:
|
||||
*
|
||||
* See #encodebin:avoid-reencoding
|
||||
*/
|
||||
g_object_class_install_property (object_class, PROP_AVOID_REENCODING,
|
||||
g_param_spec_boolean ("avoid-reencoding", "Avoid re-encoding",
|
||||
"Whether to re-encode portions of compatible video streams that lay on segment boundaries",
|
||||
DEFAULT_AVOID_REENCODING,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GstTranscodeBin:video-filter:
|
||||
*
|
||||
* Set the video filter element/bin to use.
|
||||
*/
|
||||
g_object_class_install_property (object_class, PROP_VIDEO_FILTER,
|
||||
g_param_spec_object ("video-filter", "Video filter",
|
||||
"the video filter(s) to apply, if possible",
|
||||
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* GstTranscodeBin:audio-filter:
|
||||
*
|
||||
* Set the audio filter element/bin to use.
|
||||
*/
|
||||
g_object_class_install_property (object_class, PROP_AUDIO_FILTER,
|
||||
g_param_spec_object ("audio-filter", "Audio filter",
|
||||
"the audio filter(s) to apply, if possible",
|
||||
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_transcode_bin_init (GstTranscodeBin * self)
|
||||
{
|
||||
GstPadTemplate *pad_tmpl;
|
||||
|
||||
pad_tmpl = gst_static_pad_template_get (&transcode_bin_sink_template);
|
||||
self->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", pad_tmpl);
|
||||
gst_pad_set_active (self->sinkpad, TRUE);
|
||||
gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
|
||||
|
||||
gst_object_unref (pad_tmpl);
|
||||
|
||||
pad_tmpl = gst_static_pad_template_get (&transcode_bin_src_template);
|
||||
|
||||
self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", pad_tmpl);
|
||||
gst_pad_set_active (self->srcpad, TRUE);
|
||||
gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
|
||||
|
||||
gst_object_unref (pad_tmpl);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
gst_pb_utils_init ();
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_transcodebin_debug, "transcodebin", 0,
|
||||
"Transcodebin element");
|
||||
|
||||
res &= gst_element_register (plugin, "transcodebin", GST_RANK_NONE,
|
||||
GST_TYPE_TRANSCODE_BIN);
|
||||
|
||||
res &= gst_element_register (plugin, "uritranscodebin", GST_RANK_NONE,
|
||||
gst_uri_transcode_bin_get_type ());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
transcode,
|
||||
"A plugin containing elements for transcoding", plugin_init, VERSION,
|
||||
GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|
31
gst/transcode/gsttranscoding.h
Normal file
31
gst/transcode/gsttranscoding.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
|
||||
*
|
||||
* gsttranscodebin.c:
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_TRANSCODING_H__
|
||||
#define __GST_TRANSCODING_H__
|
||||
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
GType gst_transcode_bin_get_type (void);
|
||||
GType gst_uri_transcode_bin_get_type (void);
|
||||
|
||||
#endif /* __GST_TRANSCODING_H__ */
|
562
gst/transcode/gsturitranscodebin.c
Normal file
562
gst/transcode/gsturitranscodebin.c
Normal file
|
@ -0,0 +1,562 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2019 Thibault Saunier <tsaunier@igalia.com>
|
||||
*
|
||||
* gsturitranscodebin.c:
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gsttranscoding.h"
|
||||
#if HAVE_GETRUSAGE
|
||||
#include "gst-cpu-throttling-clock.h"
|
||||
#endif
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
#include <gst/pbutils/missing-plugins.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_uri_transcodebin_debug);
|
||||
#define GST_CAT_DEFAULT gst_uri_transcodebin_debug
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstPipeline parent;
|
||||
|
||||
GstElement *src;
|
||||
gchar *source_uri;
|
||||
|
||||
GstElement *transcodebin;
|
||||
|
||||
GstElement *audio_filter;
|
||||
GstElement *video_filter;
|
||||
|
||||
GstEncodingProfile *profile;
|
||||
gboolean avoid_reencoding;
|
||||
guint wanted_cpu_usage;
|
||||
|
||||
GstElement *sink;
|
||||
gchar *dest_uri;
|
||||
|
||||
GstClock *cpu_clock;
|
||||
|
||||
} GstUriTranscodeBin;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstPipelineClass parent;
|
||||
|
||||
} GstUriTranscodeBinClass;
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#define parent_class gst_uri_transcode_bin_parent_class
|
||||
#define GST_TYPE_URI_TRANSCODE_BIN (gst_uri_transcode_bin_get_type ())
|
||||
#define GST_URI_TRANSCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_URI_TRANSCODE_BIN, GstUriTranscodeBin))
|
||||
#define GST_URI_TRANSCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_URI_TRANSCODE_BIN_TYPE, GstUriTranscodeBinClass))
|
||||
#define GST_IS_TRANSCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_URI_TRANSCODE_BIN_TYPE))
|
||||
#define GST_IS_TRANSCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_URI_TRANSCODE_BIN_TYPE))
|
||||
#define GST_URI_TRANSCODE_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_URI_TRANSCODE_BIN_TYPE, GstUriTranscodeBinClass))
|
||||
|
||||
#define DEFAULT_AVOID_REENCODING FALSE
|
||||
|
||||
G_DEFINE_TYPE (GstUriTranscodeBin, gst_uri_transcode_bin, GST_TYPE_PIPELINE)
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_PROFILE,
|
||||
PROP_SOURCE_URI,
|
||||
PROP_DEST_URI,
|
||||
PROP_AVOID_REENCODING,
|
||||
PROP_SINK,
|
||||
PROP_SRC,
|
||||
PROP_CPU_USAGE,
|
||||
PROP_VIDEO_FILTER,
|
||||
PROP_AUDIO_FILTER,
|
||||
LAST_PROP
|
||||
};
|
||||
|
||||
static void
|
||||
post_missing_plugin_error (GstElement * dec, const gchar * element_name)
|
||||
{
|
||||
GstMessage *msg;
|
||||
|
||||
msg = gst_missing_element_message_new (dec, element_name);
|
||||
gst_element_post_message (dec, msg);
|
||||
|
||||
GST_ELEMENT_ERROR (dec, CORE, MISSING_PLUGIN,
|
||||
("Missing element '%s' - check your GStreamer installation.",
|
||||
element_name), (NULL));
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
|
||||
static gboolean
|
||||
make_transcodebin (GstUriTranscodeBin * self)
|
||||
{
|
||||
GST_INFO_OBJECT (self, "making new transcodebin");
|
||||
|
||||
self->transcodebin = gst_element_factory_make ("transcodebin", NULL);
|
||||
if (!self->transcodebin)
|
||||
goto no_decodebin;
|
||||
|
||||
g_object_set (self->transcodebin, "profile", self->profile,
|
||||
"video-filter", self->video_filter,
|
||||
"audio-filter", self->audio_filter,
|
||||
"avoid-reencoding", self->avoid_reencoding, NULL);
|
||||
|
||||
gst_bin_add (GST_BIN (self), self->transcodebin);
|
||||
if (!gst_element_link (self->transcodebin, self->sink))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
no_decodebin:
|
||||
{
|
||||
post_missing_plugin_error (GST_ELEMENT_CAST (self), "transcodebin");
|
||||
|
||||
GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN, (NULL),
|
||||
("No transcodebin element, check your installation"));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
make_dest (GstUriTranscodeBin * self)
|
||||
{
|
||||
GError *err = NULL;
|
||||
|
||||
if (!gst_uri_is_valid (self->dest_uri))
|
||||
goto invalid_uri;
|
||||
|
||||
self->sink = gst_element_make_from_uri (GST_URI_SINK, self->dest_uri,
|
||||
"sink", &err);
|
||||
if (!self->sink)
|
||||
goto no_sink;
|
||||
|
||||
gst_bin_add (GST_BIN (self), self->sink);
|
||||
g_object_set (self->sink, "sync", TRUE, "max-lateness", GST_CLOCK_TIME_NONE,
|
||||
NULL);
|
||||
return TRUE;
|
||||
|
||||
invalid_uri:
|
||||
{
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||
("Invalid URI \"%s\".", self->dest_uri), (NULL));
|
||||
g_clear_error (&err);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
no_sink:
|
||||
{
|
||||
/* whoops, could not create the source element, dig a little deeper to
|
||||
* figure out what might be wrong. */
|
||||
if (err != NULL && err->code == GST_URI_ERROR_UNSUPPORTED_PROTOCOL) {
|
||||
gchar *prot;
|
||||
|
||||
prot = gst_uri_get_protocol (self->dest_uri);
|
||||
if (prot == NULL)
|
||||
goto invalid_uri;
|
||||
|
||||
gst_element_post_message (GST_ELEMENT_CAST (self),
|
||||
gst_missing_uri_source_message_new (GST_ELEMENT (self), prot));
|
||||
|
||||
GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN,
|
||||
("No URI handler implemented for \"%s\".", prot), (NULL));
|
||||
|
||||
g_free (prot);
|
||||
} else {
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||
("%s", (err) ? err->message : "URI was not accepted by any element"),
|
||||
("No element accepted URI '%s'", self->dest_uri));
|
||||
}
|
||||
|
||||
g_clear_error (&err);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
make_source (GstUriTranscodeBin * self)
|
||||
{
|
||||
GError *err = NULL;
|
||||
|
||||
if (!gst_uri_is_valid (self->source_uri))
|
||||
goto invalid_uri;
|
||||
|
||||
self->src = gst_element_make_from_uri (GST_URI_SRC, self->source_uri,
|
||||
"src", &err);
|
||||
if (!self->src)
|
||||
goto no_sink;
|
||||
|
||||
gst_bin_add (GST_BIN (self), self->src);
|
||||
|
||||
if (!gst_element_link (self->src, self->transcodebin))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
|
||||
invalid_uri:
|
||||
{
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||
("Invalid URI \"%s\".", self->source_uri), (NULL));
|
||||
g_clear_error (&err);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
no_sink:
|
||||
{
|
||||
/* whoops, could not create the source element, dig a little deeper to
|
||||
* figure out what might be wrong. */
|
||||
if (err != NULL && err->code == GST_URI_ERROR_UNSUPPORTED_PROTOCOL) {
|
||||
gchar *prot;
|
||||
|
||||
prot = gst_uri_get_protocol (self->source_uri);
|
||||
if (prot == NULL)
|
||||
goto invalid_uri;
|
||||
|
||||
gst_element_post_message (GST_ELEMENT_CAST (self),
|
||||
gst_missing_uri_source_message_new (GST_ELEMENT (self), prot));
|
||||
|
||||
GST_ELEMENT_ERROR (self, CORE, MISSING_PLUGIN,
|
||||
("No URI handler implemented for \"%s\".", prot), (NULL));
|
||||
|
||||
g_free (prot);
|
||||
} else {
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||
("%s", (err) ? err->message : "URI was not accepted by any element"),
|
||||
("No element accepted URI '%s'", self->dest_uri));
|
||||
}
|
||||
|
||||
g_clear_error (&err);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
remove_all_children (GstUriTranscodeBin * self)
|
||||
{
|
||||
if (self->sink) {
|
||||
gst_element_set_state (self->sink, GST_STATE_NULL);
|
||||
gst_bin_remove (GST_BIN (self), self->sink);
|
||||
self->sink = NULL;
|
||||
}
|
||||
|
||||
if (self->transcodebin) {
|
||||
gst_element_set_state (self->transcodebin, GST_STATE_NULL);
|
||||
gst_bin_remove (GST_BIN (self), self->transcodebin);
|
||||
self->transcodebin = NULL;
|
||||
}
|
||||
|
||||
if (self->src) {
|
||||
gst_element_set_state (self->src, GST_STATE_NULL);
|
||||
gst_bin_remove (GST_BIN (self), self->src);
|
||||
self->src = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_uri_transcode_bin_change_state (GstElement * element,
|
||||
GstStateChange transition)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
GstUriTranscodeBin *self = GST_URI_TRANSCODE_BIN (element);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||
|
||||
if (!make_dest (self))
|
||||
goto setup_failed;
|
||||
|
||||
if (!make_transcodebin (self))
|
||||
goto setup_failed;
|
||||
|
||||
if (!make_source (self))
|
||||
goto setup_failed;
|
||||
|
||||
if (gst_element_set_state (self->sink,
|
||||
GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
|
||||
GST_ERROR_OBJECT (self,
|
||||
"Could not set %" GST_PTR_FORMAT " state to PAUSED", self->sink);
|
||||
goto setup_failed;
|
||||
}
|
||||
|
||||
if (gst_element_set_state (self->transcodebin,
|
||||
GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
|
||||
GST_ERROR_OBJECT (self,
|
||||
"Could not set %" GST_PTR_FORMAT " state to PAUSED",
|
||||
self->transcodebin);
|
||||
goto setup_failed;
|
||||
}
|
||||
|
||||
if (gst_element_set_state (self->src,
|
||||
GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
|
||||
GST_ERROR_OBJECT (self,
|
||||
"Could not set %" GST_PTR_FORMAT " state to PAUSED", self->src);
|
||||
goto setup_failed;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
goto beach;
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
remove_all_children (self);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
beach:
|
||||
return ret;
|
||||
|
||||
setup_failed:
|
||||
remove_all_children (self);
|
||||
return GST_STATE_CHANGE_FAILURE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_uri_transcode_bin_constructed (GObject * object)
|
||||
{
|
||||
#if HAVE_GETRUSAGE
|
||||
GstUriTranscodeBin *self = GST_URI_TRANSCODE_BIN (object);
|
||||
|
||||
self->cpu_clock =
|
||||
GST_CLOCK (gst_cpu_throttling_clock_new (self->wanted_cpu_usage));
|
||||
gst_pipeline_use_clock (GST_PIPELINE (self), self->cpu_clock);
|
||||
#endif
|
||||
|
||||
((GObjectClass *) parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_uri_transcode_bin_dispose (GObject * object)
|
||||
{
|
||||
GstUriTranscodeBin *self = (GstUriTranscodeBin *) object;
|
||||
|
||||
g_clear_object (&self->video_filter);
|
||||
g_clear_object (&self->audio_filter);
|
||||
g_clear_object (&self->cpu_clock);
|
||||
|
||||
G_OBJECT_CLASS (gst_uri_transcode_bin_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_uri_transcode_bin_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstUriTranscodeBin *self = GST_URI_TRANSCODE_BIN (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PROFILE:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_object (value, self->profile);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_DEST_URI:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_string (value, self->dest_uri);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_SOURCE_URI:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_string (value, self->source_uri);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_AVOID_REENCODING:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_boolean (value, self->avoid_reencoding);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_CPU_USAGE:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_uint (value, self->wanted_cpu_usage);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_VIDEO_FILTER:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_object (value, self->video_filter);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_AUDIO_FILTER:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_value_set_object (value, self->audio_filter);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_uri_transcode_bin_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstUriTranscodeBin *self = GST_URI_TRANSCODE_BIN (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PROFILE:
|
||||
GST_OBJECT_LOCK (self);
|
||||
self->profile = g_value_dup_object (value);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_DEST_URI:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_free (self->dest_uri);
|
||||
self->dest_uri = g_value_dup_string (value);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_SOURCE_URI:
|
||||
GST_OBJECT_LOCK (self);
|
||||
g_free (self->source_uri);
|
||||
self->source_uri = g_value_dup_string (value);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_AVOID_REENCODING:
|
||||
GST_OBJECT_LOCK (self);
|
||||
self->avoid_reencoding = g_value_get_boolean (value);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_CPU_USAGE:
|
||||
#if HAVE_GETRUSAGE
|
||||
GST_OBJECT_LOCK (self);
|
||||
self->wanted_cpu_usage = g_value_get_uint (value);
|
||||
g_object_set (self->cpu_clock, "cpu-usage", self->wanted_cpu_usage, NULL);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
#else
|
||||
GST_ERROR_OBJECT (self,
|
||||
"No CPU usage throttling support for that platform");
|
||||
#endif
|
||||
break;
|
||||
case PROP_AUDIO_FILTER:
|
||||
GST_OBJECT_LOCK (self);
|
||||
self->audio_filter = g_value_dup_object (value);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
case PROP_VIDEO_FILTER:
|
||||
GST_OBJECT_LOCK (self);
|
||||
self->video_filter = g_value_dup_object (value);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_uri_transcode_bin_class_init (GstUriTranscodeBinClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *gstelement_klass;
|
||||
|
||||
object_class->get_property = gst_uri_transcode_bin_get_property;
|
||||
object_class->set_property = gst_uri_transcode_bin_set_property;
|
||||
object_class->constructed = gst_uri_transcode_bin_constructed;
|
||||
object_class->dispose = gst_uri_transcode_bin_dispose;
|
||||
|
||||
gstelement_klass = (GstElementClass *) klass;
|
||||
gstelement_klass->change_state =
|
||||
GST_DEBUG_FUNCPTR (gst_uri_transcode_bin_change_state);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_uri_transcodebin_debug, "uritranscodebin", 0,
|
||||
"UriTranscodebin element");
|
||||
|
||||
gst_element_class_set_static_metadata (gstelement_klass,
|
||||
"URITranscode Bin", "Generic/Bin/Encoding",
|
||||
"Autoplug and transcoder media from uris",
|
||||
"Thibault Saunier <tsaunier@igalia.com>");
|
||||
|
||||
/**
|
||||
* GstUriTranscodeBin:profile:
|
||||
*
|
||||
* The #GstEncodingProfile to use. This property must be set before going
|
||||
* to %GST_STATE_PAUSED or higher.
|
||||
*/
|
||||
g_object_class_install_property (object_class, PROP_PROFILE,
|
||||
g_param_spec_object ("profile", "Profile",
|
||||
"The GstEncodingProfile to use", GST_TYPE_ENCODING_PROFILE,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GstUriTranscodeBin:source-uri:
|
||||
*
|
||||
* The URI of the stream to encode
|
||||
*/
|
||||
g_object_class_install_property (object_class, PROP_SOURCE_URI,
|
||||
g_param_spec_string ("source-uri", "Source URI", "URI to decode",
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GstUriTranscodeBin:dest-uri:
|
||||
*
|
||||
* The destination URI to which the stream should be encoded.
|
||||
*/
|
||||
g_object_class_install_property (object_class, PROP_DEST_URI,
|
||||
g_param_spec_string ("dest-uri", "URI", "URI to put output stream",
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GstUriTranscodeBin:avoid-reencoding:
|
||||
*
|
||||
* See #encodebin:avoid-reencoding
|
||||
*/
|
||||
g_object_class_install_property (object_class, PROP_AVOID_REENCODING,
|
||||
g_param_spec_boolean ("avoid-reencoding", "Avoid re-encoding",
|
||||
"Whether to re-encode portions of compatible video streams that lay on segment boundaries",
|
||||
DEFAULT_AVOID_REENCODING,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_CPU_USAGE,
|
||||
g_param_spec_uint ("cpu-usage", "cpu-usage",
|
||||
"The percentage of CPU to try to use with the processus running the "
|
||||
"pipeline driven by the clock", 0, 100,
|
||||
100, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* GstUriTranscodeBin:video-filter:
|
||||
*
|
||||
* Set the video filter element/bin to use.
|
||||
*/
|
||||
g_object_class_install_property (object_class, PROP_VIDEO_FILTER,
|
||||
g_param_spec_object ("video-filter", "Video filter",
|
||||
"the video filter(s) to apply, if possible",
|
||||
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
/**
|
||||
* GstUriTranscodeBin:audio-filter:
|
||||
*
|
||||
* Set the audio filter element/bin to use.
|
||||
*/
|
||||
g_object_class_install_property (object_class, PROP_AUDIO_FILTER,
|
||||
g_param_spec_object ("audio-filter", "Audio filter",
|
||||
"the audio filter(s) to apply, if possible",
|
||||
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_uri_transcode_bin_init (GstUriTranscodeBin * self)
|
||||
{
|
||||
self->wanted_cpu_usage = 100;
|
||||
}
|
13
gst/transcode/meson.build
Normal file
13
gst/transcode/meson.build
Normal file
|
@ -0,0 +1,13 @@
|
|||
gsttranscoder_plugin = library('gsttranscode',
|
||||
'gsttranscodebin.c',
|
||||
'gst-cpu-throttling-clock.c',
|
||||
'gsturitranscodebin.c',
|
||||
install : true,
|
||||
dependencies : [gst_dep, gstpbutils_dep],
|
||||
c_args : gst_plugins_bad_args,
|
||||
include_directories : [configinc],
|
||||
install_dir : plugins_install_dir,
|
||||
)
|
||||
|
||||
pkgconfig.generate(gsttranscoder_plugin, install_dir : plugins_pkgconfig_install_dir)
|
||||
plugins += [gsttranscoder_plugin]
|
|
@ -147,10 +147,15 @@ check_functions = [
|
|||
['HAVE_GMTIME_R', 'gmtime_r'],
|
||||
['HAVE_MMAP', 'mmap'],
|
||||
['HAVE_PIPE2', 'pipe2'],
|
||||
['HAVE_GETRUSAGE', 'getrusage', '#include<sys/resource.h>'],
|
||||
]
|
||||
|
||||
foreach f : check_functions
|
||||
if cc.has_function(f.get(1))
|
||||
prefix = ''
|
||||
if f.length() == 3
|
||||
prefix = f.get(2)
|
||||
endif
|
||||
if cc.has_function(f.get(1), prefix: prefix)
|
||||
cdata.set(f.get(0), 1)
|
||||
endif
|
||||
endforeach
|
||||
|
@ -427,6 +432,8 @@ subdir('gst')
|
|||
subdir('sys')
|
||||
subdir('ext')
|
||||
subdir('tests')
|
||||
subdir('data')
|
||||
subdir('tools')
|
||||
subdir('pkgconfig')
|
||||
|
||||
# xgettext is optional (on Windows for instance)
|
||||
|
|
|
@ -143,6 +143,7 @@ option('srt', type : 'feature', value : 'auto', description : 'Secure, Reliable,
|
|||
option('srtp', type : 'feature', value : 'auto', description : 'Secure RTP codec plugin')
|
||||
option('teletext', type : 'feature', value : 'auto', description : 'Teletext plugin')
|
||||
option('tinyalsa', type : 'feature', value : 'auto', description : 'TinyALSA plugin')
|
||||
option('transcode', type : 'feature', value : 'auto', description : 'Transcode plugin')
|
||||
option('ttml', type : 'feature', value : 'auto', description : 'TTML subtitle parser and renderer plugin')
|
||||
option('uvch264', type : 'feature', value : 'auto', description : 'UVC compliant H.264 camera source plugin')
|
||||
option('voaacenc', type : 'feature', value : 'auto', description : 'AAC audio encoder plugin')
|
||||
|
|
12
pkgconfig/gstreamer-bad-transcoder-uninstalled.pc.in
Normal file
12
pkgconfig/gstreamer-bad-transcoder-uninstalled.pc.in
Normal file
|
@ -0,0 +1,12 @@
|
|||
prefix=
|
||||
exec_prefix=
|
||||
libdir=@transcoderlibdir@
|
||||
includedir=@abs_top_srcdir@/gst-libs
|
||||
|
||||
Name: GStreamer bad transcoder library, uninstalled
|
||||
Description: High level API for transcoding using GStreamer, uninstalled
|
||||
Version: @VERSION@
|
||||
Requires: gstreamer-@GST_API_VERSION@ gstreamer-base-@GST_API_VERSION@
|
||||
|
||||
Libs: -L${libdir} -lgsttranscoder-@GST_API_VERSION@
|
||||
Cflags: -I@abs_top_srcdir@/gst-libs -I@abs_top_builddir@/gst-libs
|
13
pkgconfig/gstreamer-bad-transcoder.pc.in
Normal file
13
pkgconfig/gstreamer-bad-transcoder.pc.in
Normal file
|
@ -0,0 +1,13 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@/gstreamer-@GST_API_VERSION@
|
||||
pluginsdir=@libdir@/gstreamer-@GST_API_VERSION@
|
||||
|
||||
Name: GStreamer bad transcoder library, uninstalled
|
||||
Description: High level API for transcoding using GStreamer
|
||||
Version: @VERSION@
|
||||
Requires: gstreamer-@GST_API_VERSION@ gstreamer-base-@GST_API_VERSION@
|
||||
|
||||
Libs: -L${libdir} -lgsttranscoder-@GST_API_VERSION@
|
||||
Cflags: -I${includedir}
|
|
@ -11,6 +11,7 @@ pkgconf.set('VERSION', gst_version)
|
|||
pkgconf.set('abs_top_builddir', join_paths(meson.current_build_dir(), '..'))
|
||||
pkgconf.set('abs_top_srcdir', join_paths(meson.current_source_dir(), '..'))
|
||||
pkgconf.set('audiolibdir', join_paths(meson.build_root(), gstbadaudio.outdir()))
|
||||
pkgconf.set('transcoderlibdir', join_paths(meson.build_root(), gst_transcoder.outdir()))
|
||||
pkgconf.set('codecparserslibdir', join_paths(meson.build_root(), gstcodecparsers.outdir()))
|
||||
pkgconf.set('insertbinlibdir', join_paths(meson.build_root(), gstinsertbin.outdir()))
|
||||
pkgconf.set('mpegtslibdir', join_paths(meson.build_root(), gstmpegts.outdir()))
|
||||
|
|
401
tools/gst-transcoder.c
Normal file
401
tools/gst-transcoder.c
Normal file
|
@ -0,0 +1,401 @@
|
|||
/* GStreamer
|
||||
*
|
||||
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include <gst/transcoder/gsttranscoder.h>
|
||||
|
||||
static const gchar *HELP_SUMMARY =
|
||||
"gst-transcoder-1.0 transcodes a stream defined by its first <input-uri>\n"
|
||||
"argument to the place defined by its second <output-uri> argument\n"
|
||||
"into the format described in its third <encoding-format> argument,\n"
|
||||
"or using the given <output-uri> file extension.\n"
|
||||
"\n"
|
||||
"The <encoding-format> argument:\n"
|
||||
"===============================\n"
|
||||
"\n"
|
||||
"If the encoding format is not defined, it will be guessed with\n"
|
||||
"the given <output-uri> file extension."
|
||||
"\n"
|
||||
"<encoding-format> describe the media format into which the\n"
|
||||
"input stream is going to be transcoded. We have two different\n"
|
||||
"ways of describing the format:\n"
|
||||
"\n"
|
||||
"GstEncodingProfile serialization format\n"
|
||||
"---------------------------------------\n"
|
||||
"\n"
|
||||
"GStreamer encoding profiles can be descibed with a quite extensive\n"
|
||||
"syntax which is descibed in the GstEncodingProfile documentation.\n"
|
||||
"\n"
|
||||
"The simple case looks like:\n"
|
||||
"\n"
|
||||
" muxer_source_caps:videoencoder_source_caps:audioencoder_source_caps\n"
|
||||
"\n"
|
||||
"Name and category of serialized GstEncodingTarget\n"
|
||||
"-------------------------------------------------\n"
|
||||
"\n"
|
||||
"Encoding targets describe well known formats which\n"
|
||||
"those are provided in '.gep' files. You can list\n"
|
||||
"available ones using the `--list-targets` argument.\n";
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gint cpu_usage, rate;
|
||||
gboolean list;
|
||||
GstEncodingProfile *profile;
|
||||
gchar *src_uri, *dest_uri, *encoding_format, *size;
|
||||
gchar *framerate;
|
||||
} Settings;
|
||||
|
||||
static void
|
||||
position_updated_cb (GstTranscoder * transcoder, GstClockTime pos)
|
||||
{
|
||||
GstClockTime dur = -1;
|
||||
gchar status[64] = { 0, };
|
||||
|
||||
g_object_get (transcoder, "duration", &dur, NULL);
|
||||
|
||||
memset (status, ' ', sizeof (status) - 1);
|
||||
|
||||
if (pos != -1 && dur > 0 && dur != -1) {
|
||||
gchar dstr[32], pstr[32];
|
||||
|
||||
/* FIXME: pretty print in nicer format */
|
||||
g_snprintf (pstr, 32, "%" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
|
||||
pstr[9] = '\0';
|
||||
g_snprintf (dstr, 32, "%" GST_TIME_FORMAT, GST_TIME_ARGS (dur));
|
||||
dstr[9] = '\0';
|
||||
g_print ("%s / %s %s\r", pstr, dstr, status);
|
||||
}
|
||||
}
|
||||
|
||||
static GList *
|
||||
get_profiles_of_type (GstEncodingProfile * profile, GType profile_type)
|
||||
{
|
||||
GList *tmp, *profiles = NULL;
|
||||
|
||||
if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) {
|
||||
for (tmp = (GList *)
|
||||
gst_encoding_container_profile_get_profiles
|
||||
(GST_ENCODING_CONTAINER_PROFILE (profile)); tmp; tmp = tmp->next) {
|
||||
if (G_OBJECT_TYPE (tmp->data) == profile_type)
|
||||
profiles = g_list_prepend (profiles, tmp->data);
|
||||
}
|
||||
} else if (GST_IS_ENCODING_VIDEO_PROFILE (profile)) {
|
||||
profiles = g_list_prepend (profiles, profile);
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_video_settings (Settings * settings)
|
||||
{
|
||||
GList *video_profiles, *tmp;
|
||||
gchar *p, *tmpstr, **vsize;
|
||||
gint width = 0, height = 0;
|
||||
GValue framerate = G_VALUE_INIT;
|
||||
|
||||
if (!settings->size && !settings->framerate)
|
||||
return TRUE;
|
||||
|
||||
if (settings->size) {
|
||||
p = tmpstr = g_strdup (settings->size);
|
||||
|
||||
for (; *p; ++p)
|
||||
*p = g_ascii_tolower (*p);
|
||||
|
||||
vsize = g_strsplit (tmpstr, "x", -1);
|
||||
g_free (tmpstr);
|
||||
|
||||
if (!vsize[1] || vsize[2]) {
|
||||
g_strfreev (vsize);
|
||||
error ("Video size should be in the form: WxH, got %s", settings->size);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
width = g_ascii_strtoull (vsize[0], NULL, 0);
|
||||
height = g_ascii_strtoull (vsize[1], NULL, 10);
|
||||
g_strfreev (vsize);
|
||||
}
|
||||
|
||||
if (settings->framerate) {
|
||||
g_value_init (&framerate, GST_TYPE_FRACTION);
|
||||
if (!gst_value_deserialize (&framerate, settings->framerate)) {
|
||||
error ("Video framerate should be either a fraction or an integer"
|
||||
" not: %s", settings->framerate);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
video_profiles = get_profiles_of_type (settings->profile,
|
||||
GST_TYPE_ENCODING_VIDEO_PROFILE);
|
||||
for (tmp = video_profiles; tmp; tmp = tmp->next) {
|
||||
GstCaps *rest = gst_encoding_profile_get_restriction (tmp->data);
|
||||
|
||||
if (!rest)
|
||||
rest = gst_caps_new_empty_simple ("video/x-raw");
|
||||
else
|
||||
rest = gst_caps_copy (rest);
|
||||
|
||||
if (settings->size) {
|
||||
gst_caps_set_simple (rest, "width", G_TYPE_INT, width,
|
||||
"height", G_TYPE_INT, height, NULL);
|
||||
}
|
||||
if (settings->framerate)
|
||||
gst_caps_set_value (rest, "framerate", &framerate);
|
||||
|
||||
gst_encoding_profile_set_restriction (tmp->data, rest);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_audio_settings (Settings * settings)
|
||||
{
|
||||
GList *audio_profiles, *tmp;
|
||||
|
||||
if (settings->rate < 0)
|
||||
return TRUE;
|
||||
|
||||
audio_profiles =
|
||||
get_profiles_of_type (settings->profile, GST_TYPE_ENCODING_AUDIO_PROFILE);
|
||||
for (tmp = audio_profiles; tmp; tmp = tmp->next) {
|
||||
GstCaps *rest = gst_encoding_profile_get_restriction (tmp->data);
|
||||
|
||||
if (!rest)
|
||||
rest = gst_caps_new_empty_simple ("audio/x-raw");
|
||||
else
|
||||
rest = gst_caps_copy (rest);
|
||||
|
||||
gst_caps_set_simple (rest, "rate", G_TYPE_INT, settings->rate, NULL);
|
||||
gst_encoding_profile_set_restriction (tmp->data, rest);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
list_encoding_targets (void)
|
||||
{
|
||||
GList *tmp, *targets = gst_encoding_list_all_targets (NULL);
|
||||
|
||||
for (tmp = targets; tmp; tmp = tmp->next) {
|
||||
GstEncodingTarget *target = tmp->data;
|
||||
GList *usable_profiles = get_usable_profiles (target);
|
||||
|
||||
if (usable_profiles) {
|
||||
GList *tmpprof;
|
||||
|
||||
g_print ("\n%s (%s): %s\n * Profiles:\n",
|
||||
gst_encoding_target_get_name (target),
|
||||
gst_encoding_target_get_category (target),
|
||||
gst_encoding_target_get_description (target));
|
||||
|
||||
for (tmpprof = usable_profiles; tmpprof; tmpprof = tmpprof->next)
|
||||
g_print (" - %s: %s",
|
||||
gst_encoding_profile_get_name (tmpprof->data),
|
||||
gst_encoding_profile_get_description (tmpprof->data));
|
||||
|
||||
g_print ("\n");
|
||||
g_list_free (usable_profiles);
|
||||
}
|
||||
}
|
||||
|
||||
g_list_free_full (targets, (GDestroyNotify) g_object_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
_error_cb (GstTranscoder * transcoder, GError * err, GstStructure * details)
|
||||
{
|
||||
if (g_error_matches (err, GST_CORE_ERROR, GST_CORE_ERROR_PAD)) {
|
||||
GstPadLinkReturn lret;
|
||||
GType type;
|
||||
|
||||
if (details && gst_structure_get (details, "linking-error",
|
||||
GST_TYPE_PAD_LINK_RETURN, &lret,
|
||||
"msg-source-type", G_TYPE_GTYPE, &type, NULL) &&
|
||||
type == g_type_from_name ("GstTranscodeBin")) {
|
||||
error ("\nCould not setup transcoding pipeline,"
|
||||
" make sure that you transcoding format parametters"
|
||||
" are compatible with the input stream.");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
error ("\nFAILURE: %s", err->message);
|
||||
}
|
||||
|
||||
static void
|
||||
_warning_cb (GstTranscoder * transcoder, GError * error, GstStructure * details)
|
||||
{
|
||||
gboolean cant_encode;
|
||||
GstCaps *caps = NULL;
|
||||
gchar *stream_id = NULL;
|
||||
|
||||
if (details && gst_structure_get (details, "can-t-encode-stream",
|
||||
G_TYPE_BOOLEAN, &cant_encode, "stream-caps", GST_TYPE_CAPS,
|
||||
&caps, "stream-id", G_TYPE_STRING, &stream_id, NULL)) {
|
||||
gchar *source_uri = gst_transcoder_get_source_uri (transcoder);
|
||||
|
||||
warn ("WARNING: Input stream %s: WON'T BE ENCODED.\n"
|
||||
"Make sure the encoding settings are valid and that"
|
||||
" any preset you set actually exists.\n"
|
||||
"For more information about that stream, you can inspect"
|
||||
" the source stream with:\n\n"
|
||||
" gst-discoverer-1.0 -v %s\n", stream_id, source_uri);
|
||||
gst_caps_unref (caps);
|
||||
g_free (stream_id);;
|
||||
g_free (source_uri);;
|
||||
|
||||
return;
|
||||
}
|
||||
warn ("Got warning: %s", error->message);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
gint res = 0;
|
||||
GError *err = NULL;
|
||||
GstTranscoder *transcoder;
|
||||
GOptionContext *ctx;
|
||||
Settings settings = {
|
||||
.cpu_usage = 100,
|
||||
.rate = -1,
|
||||
.encoding_format = NULL,
|
||||
.size = NULL,
|
||||
.framerate = NULL,
|
||||
};
|
||||
GOptionEntry options[] = {
|
||||
{"cpu-usage", 'c', 0, G_OPTION_ARG_INT, &settings.cpu_usage,
|
||||
"The CPU usage to target in the transcoding process", NULL},
|
||||
{"list-targets", 'l', G_OPTION_ARG_NONE, 0, &settings.list,
|
||||
"List all encoding targets", NULL},
|
||||
{"size", 's', 0, G_OPTION_ARG_STRING, &settings.size,
|
||||
"set frame size (WxH or abbreviation)", NULL},
|
||||
{"audio-rate", 'r', 0, G_OPTION_ARG_INT, &settings.rate,
|
||||
"set audio sampling rate (in Hz)", NULL},
|
||||
{"framerate", 'f', 0, G_OPTION_ARG_STRING, &settings.framerate,
|
||||
"set video framerate as a fraction (24/1 for 24fps)"
|
||||
" or a single number (24 for 24fps))", NULL},
|
||||
{"video-encoder", 'v', 0, G_OPTION_ARG_STRING, &settings.size,
|
||||
"The video encoder to use.", NULL},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
g_set_prgname ("gst-transcoder");
|
||||
|
||||
ctx = g_option_context_new ("<source uri> <destination uri> "
|
||||
"[<encoding format>[/<encoding profile name>]]");
|
||||
g_option_context_set_summary (ctx, HELP_SUMMARY);
|
||||
|
||||
g_option_context_add_main_entries (ctx, options, NULL);
|
||||
g_option_context_add_group (ctx, gst_init_get_option_group ());
|
||||
|
||||
if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
|
||||
g_print ("Error initializing: %s\n", GST_STR_NULL (err->message));
|
||||
g_clear_error (&err);
|
||||
g_option_context_free (ctx);
|
||||
return 1;
|
||||
}
|
||||
gst_pb_utils_init ();
|
||||
|
||||
if (settings.list) {
|
||||
list_encoding_targets ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc < 3 || argc > 4) {
|
||||
g_print ("%s", g_option_context_get_help (ctx, TRUE, NULL));
|
||||
g_option_context_free (ctx);
|
||||
|
||||
return -1;
|
||||
}
|
||||
g_option_context_free (ctx);
|
||||
|
||||
settings.src_uri = ensure_uri (argv[1]);
|
||||
settings.dest_uri = ensure_uri (argv[2]);
|
||||
|
||||
if (argc == 3) {
|
||||
settings.encoding_format = get_file_extension (settings.dest_uri);
|
||||
|
||||
if (!settings.encoding_format)
|
||||
goto no_extension;
|
||||
} else {
|
||||
settings.encoding_format = argv[3];
|
||||
}
|
||||
|
||||
settings.profile = create_encoding_profile (settings.encoding_format);
|
||||
|
||||
if (!settings.profile) {
|
||||
error ("Could not find any encoding format for %s\n",
|
||||
settings.encoding_format);
|
||||
warn ("You can list available targets using %s --list-targets", argv[0]);
|
||||
res = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
g_print ("Encoding to:\n\n");
|
||||
describe_encoding_profile (settings.profile);
|
||||
if (!set_video_settings (&settings)) {
|
||||
res = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!set_audio_settings (&settings)) {
|
||||
res = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
transcoder = gst_transcoder_new_full (settings.src_uri, settings.dest_uri,
|
||||
settings.profile, NULL);
|
||||
gst_transcoder_set_avoid_reencoding (transcoder, TRUE);
|
||||
|
||||
gst_transcoder_set_cpu_usage (transcoder, settings.cpu_usage);
|
||||
g_signal_connect (transcoder, "position-updated",
|
||||
G_CALLBACK (position_updated_cb), NULL);
|
||||
g_signal_connect (transcoder, "warning", G_CALLBACK (_warning_cb), NULL);
|
||||
g_signal_connect (transcoder, "error", G_CALLBACK (_error_cb), NULL);
|
||||
|
||||
g_assert (transcoder);
|
||||
|
||||
ok ("Starting transcoding...");
|
||||
gst_transcoder_run (transcoder, &err);
|
||||
if (!err)
|
||||
ok ("\nDONE.");
|
||||
|
||||
done:
|
||||
g_free (settings.dest_uri);
|
||||
g_free (settings.src_uri);
|
||||
|
||||
return res;
|
||||
|
||||
no_extension:
|
||||
error ("No <encoding-format> specified and no extension"
|
||||
" available in the output target: %s", settings.dest_uri);
|
||||
res = 1;
|
||||
|
||||
goto done;
|
||||
}
|
5
tools/meson.build
Normal file
5
tools/meson.build
Normal file
|
@ -0,0 +1,5 @@
|
|||
executable('gst-transcoder-' + api_version,
|
||||
'gst-transcoder.c', 'utils.c',
|
||||
install : true,
|
||||
dependencies : [gst_dep, gstpbutils_dep, gst_transcoder_dep],
|
||||
)
|
208
tools/utils.c
Normal file
208
tools/utils.c
Normal file
|
@ -0,0 +1,208 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include <gst/pbutils/descriptions.h>
|
||||
|
||||
void
|
||||
print (GstDebugColorFlags c, gboolean err, gboolean nline, const gchar * format,
|
||||
va_list var_args)
|
||||
{
|
||||
GString *str = g_string_new (NULL);
|
||||
GstDebugColorMode color_mode;
|
||||
gchar *color = NULL;
|
||||
const gchar *clear = NULL;
|
||||
|
||||
color_mode = gst_debug_get_color_mode ();
|
||||
#ifdef G_OS_WIN32
|
||||
if (color_mode == GST_DEBUG_COLOR_MODE_UNIX) {
|
||||
#else
|
||||
if (color_mode != GST_DEBUG_COLOR_MODE_OFF) {
|
||||
#endif
|
||||
clear = "\033[00m";
|
||||
color = gst_debug_construct_term_color (c);
|
||||
}
|
||||
|
||||
if (color) {
|
||||
g_string_append (str, color);
|
||||
g_free (color);
|
||||
}
|
||||
|
||||
g_string_append_vprintf (str, format, var_args);
|
||||
|
||||
if (nline)
|
||||
g_string_append_c (str, '\n');
|
||||
|
||||
if (clear)
|
||||
g_string_append (str, clear);
|
||||
|
||||
if (err)
|
||||
g_printerr ("%s", str->str);
|
||||
else
|
||||
g_print ("%s", str->str);
|
||||
|
||||
g_string_free (str, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
ok (const gchar * format, ...)
|
||||
{
|
||||
va_list var_args;
|
||||
|
||||
va_start (var_args, format);
|
||||
print (GST_DEBUG_FG_GREEN, FALSE, TRUE, format, var_args);
|
||||
va_end (var_args);
|
||||
}
|
||||
|
||||
void
|
||||
warn (const gchar * format, ...)
|
||||
{
|
||||
va_list var_args;
|
||||
|
||||
va_start (var_args, format);
|
||||
print (GST_DEBUG_FG_YELLOW, TRUE, TRUE, format, var_args);
|
||||
va_end (var_args);
|
||||
}
|
||||
|
||||
void
|
||||
error (const gchar * format, ...)
|
||||
{
|
||||
va_list var_args;
|
||||
|
||||
va_start (var_args, format);
|
||||
print (GST_DEBUG_FG_RED, TRUE, TRUE, format, var_args);
|
||||
va_end (var_args);
|
||||
}
|
||||
|
||||
gchar *
|
||||
ensure_uri (const gchar * location)
|
||||
{
|
||||
if (gst_uri_is_valid (location))
|
||||
return g_strdup (location);
|
||||
else
|
||||
return gst_filename_to_uri (location, NULL);
|
||||
}
|
||||
|
||||
gchar *
|
||||
get_file_extension (gchar * uri)
|
||||
{
|
||||
size_t len;
|
||||
gint find;
|
||||
|
||||
len = strlen (uri);
|
||||
find = len - 1;
|
||||
|
||||
while (find >= 0) {
|
||||
if (uri[find] == '.')
|
||||
break;
|
||||
find--;
|
||||
}
|
||||
|
||||
if (find < 0)
|
||||
return NULL;
|
||||
|
||||
return &uri[find + 1];
|
||||
}
|
||||
|
||||
GList *
|
||||
get_usable_profiles (GstEncodingTarget * target)
|
||||
{
|
||||
GList *tmpprof, *usable_profiles = NULL;
|
||||
|
||||
for (tmpprof = (GList *) gst_encoding_target_get_profiles (target);
|
||||
tmpprof; tmpprof = tmpprof->next) {
|
||||
GstEncodingProfile *profile = tmpprof->data;
|
||||
GstElement *tmpencodebin = gst_element_factory_make ("encodebin", NULL);
|
||||
|
||||
gst_encoding_profile_set_presence (profile, 1);
|
||||
if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) {
|
||||
GList *tmpsubprof;
|
||||
for (tmpsubprof = (GList *)
|
||||
gst_encoding_container_profile_get_profiles
|
||||
(GST_ENCODING_CONTAINER_PROFILE (profile)); tmpsubprof;
|
||||
tmpsubprof = tmpsubprof->next)
|
||||
gst_encoding_profile_set_presence (tmpsubprof->data, 1);
|
||||
}
|
||||
|
||||
g_object_set (tmpencodebin, "profile", gst_object_ref (profile), NULL);
|
||||
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (tmpencodebin),
|
||||
GST_DEBUG_GRAPH_SHOW_ALL, gst_encoding_profile_get_name (profile));
|
||||
|
||||
/* The profile could be expended */
|
||||
if (GST_BIN (tmpencodebin)->children)
|
||||
usable_profiles = g_list_prepend (usable_profiles, profile);
|
||||
|
||||
gst_object_unref (tmpencodebin);
|
||||
}
|
||||
|
||||
return usable_profiles;
|
||||
}
|
||||
|
||||
GstEncodingProfile *
|
||||
create_encoding_profile (const gchar * pname)
|
||||
{
|
||||
GstEncodingProfile *profile;
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
g_value_init (&value, GST_TYPE_ENCODING_PROFILE);
|
||||
|
||||
if (!gst_value_deserialize (&value, pname)) {
|
||||
g_value_reset (&value);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
profile = g_value_dup_object (&value);
|
||||
g_value_reset (&value);
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
get_profile_type (GstEncodingProfile * profile)
|
||||
{
|
||||
if (GST_IS_ENCODING_CONTAINER_PROFILE (profile))
|
||||
return "Container";
|
||||
else if (GST_IS_ENCODING_AUDIO_PROFILE (profile))
|
||||
return "Audio";
|
||||
else if (GST_IS_ENCODING_VIDEO_PROFILE (profile))
|
||||
return "Video";
|
||||
else
|
||||
return "Unkonwn";
|
||||
}
|
||||
|
||||
static void
|
||||
print_profile (GstEncodingProfile * profile, const gchar * prefix)
|
||||
{
|
||||
const gchar *name = gst_encoding_profile_get_name (profile);
|
||||
const gchar *desc = gst_encoding_profile_get_description (profile);
|
||||
GstCaps *format = gst_encoding_profile_get_format (profile);
|
||||
gchar *capsdesc;
|
||||
|
||||
if (gst_caps_is_fixed (format))
|
||||
capsdesc = gst_pb_utils_get_codec_description (format);
|
||||
else
|
||||
capsdesc = gst_caps_to_string (format);
|
||||
|
||||
g_print ("%s%s: %s%s%s%s%s%s\n", prefix, get_profile_type (profile),
|
||||
name ? name : capsdesc, desc ? ": " : "", desc ? desc : "",
|
||||
name ? " (" : "", name ? capsdesc : "", name ? ")" : "");
|
||||
|
||||
g_free (capsdesc);
|
||||
}
|
||||
|
||||
void
|
||||
describe_encoding_profile (GstEncodingProfile * profile)
|
||||
{
|
||||
g_return_if_fail (GST_IS_ENCODING_PROFILE (profile));
|
||||
|
||||
print_profile (profile, " ");
|
||||
if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) {
|
||||
const GList *tmp;
|
||||
|
||||
for (tmp =
|
||||
gst_encoding_container_profile_get_profiles
|
||||
(GST_ENCODING_CONTAINER_PROFILE (profile)); tmp; tmp = tmp->next)
|
||||
print_profile (tmp->data, " - ");
|
||||
}
|
||||
|
||||
}
|
21
tools/utils.h
Normal file
21
tools/utils.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef __GST_TRANSCODER_UTILS_H
|
||||
#define __GST_TRANSCODER_UTILS_H
|
||||
|
||||
#include "utils.h"
|
||||
#include <gst/gst.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
#include <gst/pbutils/encoding-profile.h>
|
||||
|
||||
void print (GstDebugColorFlags c, gboolean err, gboolean nline, const gchar * format, va_list var_args);
|
||||
void ok (const gchar * format, ...);
|
||||
void warn (const gchar * format, ...);
|
||||
void error (const gchar * format, ...);
|
||||
|
||||
gchar * ensure_uri (const gchar * location);
|
||||
gchar * get_file_extension (gchar * uri);
|
||||
|
||||
GList * get_usable_profiles (GstEncodingTarget * target);
|
||||
GstEncodingProfile * create_encoding_profile (const gchar * pname);
|
||||
void describe_encoding_profile (GstEncodingProfile *profile);
|
||||
|
||||
#endif /*__GST_TRANSCODER_UTILS_H*/
|
Loading…
Reference in a new issue