summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/meson.build22
-rw-r--r--data/targets/device/dvd.gep24
-rw-r--r--data/targets/file-extension/avi.gep21
-rw-r--r--data/targets/file-extension/flv.gep32
-rw-r--r--data/targets/file-extension/mkv.gep28
-rw-r--r--data/targets/file-extension/mp3.gep15
-rw-r--r--data/targets/file-extension/mp4.gep34
-rw-r--r--data/targets/file-extension/oga.gep15
-rw-r--r--data/targets/file-extension/ogv.gep25
-rw-r--r--data/targets/file-extension/webm.gep21
-rw-r--r--data/targets/online-service/youtube.gep24
-rw-r--r--docs/libs/gst-plugins-bad-libs-sections.txt1140
-rw-r--r--docs/libs/transcoder/index.md8
-rw-r--r--docs/libs/transcoder/sitemap.txt1
-rw-r--r--docs/meson.build1
-rw-r--r--gst-libs/gst/meson.build1
-rw-r--r--gst-libs/gst/transcoder/gsttranscoder.c1609
-rw-r--r--gst-libs/gst/transcoder/gsttranscoder.h141
-rw-r--r--gst-libs/gst/transcoder/meson.build33
-rw-r--r--gst-libs/gst/transcoder/transcoder-prelude.h36
-rw-r--r--gst/meson.build2
-rw-r--r--gst/transcode/gst-cpu-throttling-clock.c220
-rw-r--r--gst/transcode/gst-cpu-throttling-clock.h60
-rw-r--r--gst/transcode/gsttranscodebin.c614
-rw-r--r--gst/transcode/gsttranscoding.h31
-rw-r--r--gst/transcode/gsturitranscodebin.c562
-rw-r--r--gst/transcode/meson.build13
-rw-r--r--meson.build9
-rw-r--r--meson_options.txt1
-rw-r--r--pkgconfig/gstreamer-bad-transcoder-uninstalled.pc.in12
-rw-r--r--pkgconfig/gstreamer-bad-transcoder.pc.in13
-rw-r--r--pkgconfig/meson.build1
-rw-r--r--tools/gst-transcoder.c401
-rw-r--r--tools/meson.build5
-rw-r--r--tools/utils.c208
-rw-r--r--tools/utils.h21
36 files changed, 4262 insertions, 1142 deletions
diff --git a/data/meson.build b/data/meson.build
new file mode 100644
index 000000000..4a2d306ef
--- /dev/null
+++ b/data/meson.build
@@ -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
diff --git a/data/targets/device/dvd.gep b/data/targets/device/dvd.gep
new file mode 100644
index 000000000..d37604721
--- /dev/null
+++ b/data/targets/device/dvd.gep
@@ -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
diff --git a/data/targets/file-extension/avi.gep b/data/targets/file-extension/avi.gep
new file mode 100644
index 000000000..7131ba414
--- /dev/null
+++ b/data/targets/file-extension/avi.gep
@@ -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
+
diff --git a/data/targets/file-extension/flv.gep b/data/targets/file-extension/flv.gep
new file mode 100644
index 000000000..59380ce77
--- /dev/null
+++ b/data/targets/file-extension/flv.gep
@@ -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
diff --git a/data/targets/file-extension/mkv.gep b/data/targets/file-extension/mkv.gep
new file mode 100644
index 000000000..1f2c562d4
--- /dev/null
+++ b/data/targets/file-extension/mkv.gep
@@ -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
diff --git a/data/targets/file-extension/mp3.gep b/data/targets/file-extension/mp3.gep
new file mode 100644
index 000000000..74b191f48
--- /dev/null
+++ b/data/targets/file-extension/mp3.gep
@@ -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
diff --git a/data/targets/file-extension/mp4.gep b/data/targets/file-extension/mp4.gep
new file mode 100644
index 000000000..1ea3cf620
--- /dev/null
+++ b/data/targets/file-extension/mp4.gep
@@ -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
diff --git a/data/targets/file-extension/oga.gep b/data/targets/file-extension/oga.gep
new file mode 100644
index 000000000..0f80a585a
--- /dev/null
+++ b/data/targets/file-extension/oga.gep
@@ -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
diff --git a/data/targets/file-extension/ogv.gep b/data/targets/file-extension/ogv.gep
new file mode 100644
index 000000000..bf3cf77a2
--- /dev/null
+++ b/data/targets/file-extension/ogv.gep
@@ -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
diff --git a/data/targets/file-extension/webm.gep b/data/targets/file-extension/webm.gep
new file mode 100644
index 000000000..526cf4784
--- /dev/null
+++ b/data/targets/file-extension/webm.gep
@@ -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
diff --git a/data/targets/online-service/youtube.gep b/data/targets/online-service/youtube.gep
new file mode 100644
index 000000000..2d847ee54
--- /dev/null
+++ b/data/targets/online-service/youtube.gep
@@ -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
diff --git a/docs/libs/gst-plugins-bad-libs-sections.txt b/docs/libs/gst-plugins-bad-libs-sections.txt
deleted file mode 100644
index cc229a96a..000000000
--- a/docs/libs/gst-plugins-bad-libs-sections.txt
+++ /dev/null
@@ -1,1140 +0,0 @@
-# codecparsers
-<SECTION>
-<FILE>gsth264parser</FILE>
-<TITLE>h264parser</TITLE>
-<INCLUDE>gst/codecparsers/gsth264parser.h</INCLUDE>
-GST_H264_MAX_SPS_COUNT
-GST_H264_MAX_PPS_COUNT
-GST_H264_IS_P_SLICE
-GST_H264_IS_B_SLICE
-GST_H264_IS_I_SLICE
-GST_H264_IS_SP_SLICE
-GST_H264_IS_SI_SLICE
-GstH264NalUnitType
-GstH264ParserResult
-GstH264SEIPayloadType
-GstH264SEIPicStructType
-GstH264SliceType
-GstH264NalParser
-GstH264NalUnit
-GstH264SPS
-GstH264PPS
-GstH264HRDParams
-GstH264VUIParams
-GstH264DecRefPicMarking
-GstH264RefPicMarking
-GstH264PredWeightTable
-GstH264SliceHdr
-GstH264ClockTimestamp
-GstH264PicTiming
-GstH264BufferingPeriod
-GstH264SEIMessage
-gst_h264_parser_identify_nalu
-gst_h264_parser_identify_nalu_avc
-gst_h264_parser_parse_nal
-gst_h264_parser_parse_slice_hdr
-gst_h264_parser_parse_sps
-gst_h264_parser_parse_pps
-gst_h264_parser_parse_sei
-gst_h264_nal_parser_new
-gst_h264_nal_parser_free
-gst_h264_parse_sps
-gst_h264_parse_pps
-gst_h264_pps_clear
-gst_h264_quant_matrix_8x8_get_zigzag_from_raster
-gst_h264_quant_matrix_8x8_get_raster_from_zigzag
-gst_h264_quant_matrix_4x4_get_zigzag_from_raster
-gst_h264_quant_matrix_4x4_get_raster_from_zigzag
-gst_h264_video_calculate_framerate
-<SUBSECTION Standard>
-<SUBSECTION Private>
-</SECTION>
-
-<SECTION>
-<FILE>gstjpegparser</FILE>
-<TITLE>jpegparser</TITLE>
-<INCLUDE>gst/codecparsers/gstjpegparser.h</INCLUDE>
-GST_JPEG_MAX_FRAME_COMPONENTS
-GST_JPEG_MAX_SCAN_COMPONENTS
-GST_JPEG_MAX_QUANT_ELEMENTS
-GstJpegMarker;
-GST_JPEG_MARKER_SOF_MIN
-GST_JPEG_MARKER_SOF_MAX
-GST_JPEG_MARKER_APP_MIN
-GST_JPEG_MARKER_APP_MAX
-GST_JPEG_MARKER_RST_MIN
-GST_JPEG_MARKER_RST_MAX
-GstJpegEntropyCodingMode
-GstJpegProfile
-GstJpegSegment
-gst_jpeg_parse
-GstJpegFrameHdr
-GstJpegFrameComponent
-gst_jpeg_segment_parse_frame_header
-GstJpegScanHdr
-GstJpegScanComponent
-gst_jpeg_segment_parse_scan_header
-GstJpegHuffmanTables
-GstJpegHuffmanTable
-gst_jpeg_segment_parse_huffman_table
-GstJpegQuantTable
-gst_jpeg_segment_parse_quantization_table
-gst_jpeg_segment_parse_restart_interval
-gst_jpeg_get_default_quantization_tables
-gst_jpeg_get_default_huffman_tables
-<SUBSECTION Standard>
-<SUBSECTION Private>
-</SECTION>
-
-<SECTION>
-<FILE>gstvc1parser</FILE>
-<TITLE>vc1parser</TITLE>
-<INCLUDE>gst/codecparsers/gstvc1parser.h</INCLUDE>
-MAX_HRD_NUM_LEAKY_BUCKETS
-GST_VC1_BFRACTION_BASIS
-GstVC1StartCode
-GstVC1Profile
-GstVC1ParserResult
-GstVC1PictureType
-GstVC1Level
-GstVC1QuantizerSpec
-GstVC1DQProfile
-GstVC1Condover
-GstVC1MvMode
-GstVC1SeqHdr
-GstVC1AdvancedSeqHdr
-GstVC1SeqLayer
-GstVC1SeqStructA
-GstVC1SeqStructB
-GstVC1SeqStructC
-GstVC1HrdParam
-GstVC1EntryPointHdr
-GstVC1FrameHdr
-GstVC1PicAdvanced
-GstVC1PicSimpleMain
-GstVC1Picture
-GstVC1VopDquant
-GstVC1BDU
-gst_vc1_identify_next_bdu
-gst_vc1_parse_sequence_header
-gst_vc1_parse_sequence_layer
-gst_vc1_parse_sequence_header_struct_a
-gst_vc1_parse_sequence_header_struct_b
-gst_vc1_parse_sequence_header_struct_c
-gst_vc1_parse_entry_point_header
-gst_vc1_parse_frame_header
-gst_vc1_bitplanes_new
-gst_vc1_bitplanes_free
-gst_vc1_bitplanes_free_1
-gst_vc1_bitplanes_ensure_size
-<SUBSECTION Standard>
-<SUBSECTION Private>
-</SECTION>
-
-<SECTION>
-<FILE>gstmpegvideometa</FILE>
-<INCLUDE>gst/codecparsers/gstmpegvideometa.h</INCLUDE>
-GST_MPEG_VIDEO_META_API_TYPE
-GST_MPEG_VIDEO_META_INFO
-GstMpegVideoMeta
-gst_buffer_add_mpeg_video_meta
-gst_buffer_get_mpeg_video_meta
-gst_mpeg_video_meta_get_info
-<SUBSECTION Standard>
-gst_mpeg_video_meta_api_get_type
-</SECTION>
-
-
-<SECTION>
-<FILE>gstmpegvideoparser</FILE>
-<TITLE>mpegvideoparser</TITLE>
-<INCLUDE>gst/codecparsers/gstmpegvideoparser.h</INCLUDE>
-GstMpegVideoPacketTypeCode
-GstMpegVideoPacketExtensionCode
-GstMpegVideoLevel
-GstMpegVideoProfile
-GstMpegVideoPictureType
-GstMpegVideoPictureStructure
-GstMpegVideoSequenceHdr
-GstMpegVideoSequenceExt
-GstMpegVideoPictureHdr
-GstMpegVideoGop
-GstMpegVideoPictureExt
-GstMpegVideoQuantMatrixExt
-GstMpegVideoTypeOffsetSize
-gst_mpeg_video_parse
-gst_mpeg_video_packet_parse_sequence_header
-gst_mpeg_video_packet_parse_sequence_extension
-gst_mpeg_video_packet_parse_sequence_display_extension
-gst_mpeg_video_packet_parse_sequence_scalable_extension
-gst_mpeg_video_packet_parse_picture_header
-gst_mpeg_video_packet_parse_picture_extension
-gst_mpeg_video_packet_parse_gop
-gst_mpeg_video_packet_parse_slice_header
-gst_mpeg_video_packet_parse_quant_matrix_extension
-gst_mpeg_video_finalise_mpeg2_sequence_header
-gst_mpeg_video_quant_matrix_get_raster_from_zigzag
-gst_mpeg_video_quant_matrix_get_zigzag_from_raster
-<SUBSECTION Standard>
-<SUBSECTION Private>
-</SECTION>
-
-<SECTION>
-<FILE>gstmpeg4parser</FILE>
-<TITLE>mpeg4parser</TITLE>
-<INCLUDE>gst/codecparsers/gstmpeg4parser.h</INCLUDE>
-GstMpeg4StartCode
-GstMpeg4VisualObjectType
-GstMpeg4AspectRatioInfo
-GstMpeg4ParseResult
-GstMpeg4VideoObjectCodingType
-GstMpeg4ChromaFormat
-GstMpeg4VideoObjectLayerShape
-GstMpeg4SpriteEnable
-GstMpeg4Profile
-GstMpeg4Level
-GstMpeg4VisualObjectSequence
-GstMpeg4VisualObject
-GstMpeg4VideoSignalType
-GstMpeg4VideoPlaneShortHdr
-GstMpeg4VideoObjectLayer
-GstMpeg4SpriteTrajectory
-GstMpeg4GroupOfVOP
-GstMpeg4VideoObjectPlane
-GstMpeg4Packet
-GstMpeg4VideoPacketHdr
-gst_mpeg4_parse
-gst_mpeg4_parse_video_object_plane
-gst_mpeg4_parse_group_of_vop
-gst_mpeg4_parse_video_object_layer
-gst_mpeg4_parse_visual_object
-gst_mpeg4_parse_visual_object_sequence
-gst_mpeg4_parse_video_packet_header
-<SUBSECTION Standard>
-<SUBSECTION Private>
-</SECTION>
-
-<SECTION>
-<FILE>gstmpegts</FILE>
-<SUBSECTION Common>
-gst_mpegts_initialize
-</SECTION>
-
-<SECTION>
-<FILE>gstmpegtsdescriptor</FILE>
-<SUBSECTION Common>
-GstMpegtsDescriptor
-GstMpegtsDescriptorType
-GstMpegtsMiscDescriptorType
-gst_mpegts_find_descriptor
-gst_mpegts_parse_descriptors
-gst_mpegts_descriptor_from_custom
-<SUBSECTION registration>
-gst_mpegts_descriptor_from_registration
-<SUBSECTION iso639>
-GstMpegtsISO639LanguageDescriptor
-GstMpegtsIso639AudioType
-gst_mpegts_descriptor_parse_iso_639_language
-gst_mpegts_descriptor_parse_iso_639_language_idx
-gst_mpegts_descriptor_parse_iso_639_language_nb
-gst_mpegts_iso_639_language_descriptor_free
-<SUBSECTION logical_channel>
-GstMpegtsLogicalChannel
-GstMpegtsLogicalChannelDescriptor
-gst_mpegts_descriptor_parse_logical_channel
-<SUBSECTION Standard>
-GST_TYPE_MPEGTS_DVB_CODE_RATE
-GST_TYPE_MPEGTS_CABLE_OUTER_FEC_SCHEME
-GST_TYPE_MPEGTS_MODULATION_TYPE
-GST_TYPE_MPEGTS_SATELLITE_POLARIZATION_TYPE
-GST_TYPE_MPEGTS_SATELLITE_ROLLOFF
-GST_TYPE_MPEGTS_ISO_639_LANGUAGE
-GST_TYPE_MPEGTS_DESCRIPTOR
-GST_TYPE_MPEGTS_DVB_SERVICE_TYPE
-GST_TYPE_MPEGTS_DESCRIPTOR_TYPE
-GST_TYPE_MPEGTS_ISO639_AUDIO_TYPE
-GST_TYPE_MPEGTS_ATSC_DESCRIPTOR_TYPE
-GST_TYPE_MPEGTS_DVB_DESCRIPTOR_TYPE
-GST_TYPE_MPEGTS_ISDB_DESCRIPTOR_TYPE
-GST_TYPE_MPEGTS_MISC_DESCRIPTOR_TYPE
-gst_mpegts_descriptor_get_type
-gst_mpegts_iso_639_language_get_type
-gst_mpegts_cable_outer_fec_scheme_get_type
-gst_mpegts_modulation_type_get_type
-gst_mpegts_satellite_polarization_type_get_type
-gst_mpegts_satellite_rolloff_get_type
-gst_mpegts_dvb_code_rate_get_type
-gst_mpegts_descriptor_type_get_type
-gst_mpegts_atsc_descriptor_type_get_type
-gst_mpegts_dvb_descriptor_type_get_type
-gst_mpegts_isdb_descriptor_type_get_type
-gst_mpegts_misc_descriptor_type_get_type
-gst_mpegts_iso639_audio_type_get_type
-gst_mpegts_dvb_service_type_get_type
-</SECTION>
-
-<SECTION>
-<FILE>gst-atsc-descriptor</FILE>
-GstMpegtsATSCDescriptorType
-<SUBSECTION Standard>
-GST_TYPE_MPEGTS_ATSC_DESCRIPTOR_TYPE
-gst_mpegts_atsc_descriptor_type_get_type
-</SECTION>
-
-<SECTION>
-<FILE>gst-dvb-descriptor</FILE>
-GstMpegtsDVBDescriptorType
-GstMpegtsDVBExtendedDescriptorType
-<SUBSECTION content>
-GstMpegtsContent
-gst_mpegts_descriptor_parse_dvb_content
-<SUBSECTION component>
-GstMpegtsComponentDescriptor
-gst_mpegts_dvb_component_descriptor_free
-gst_mpegts_descriptor_parse_dvb_component
-<SUBSECTION extended_event>
-GstMpegtsExtendedEventItem
-GstMpegtsExtendedEventDescriptor
-gst_mpegts_extended_event_descriptor_free
-gst_mpegts_descriptor_parse_dvb_extended_event
-<SUBSECTION satellite_delivery>
-GstMpegtsSatelliteDeliverySystemDescriptor
-GstMpegtsDVBCodeRate
-GstMpegtsModulationType
-GstMpegtsSatellitePolarizationType
-GstMpegtsSatelliteRolloff
-gst_mpegts_descriptor_parse_satellite_delivery_system
-<SUBSECTION cable_delivery>
-GstMpegtsCableDeliverySystemDescriptor
-GstMpegtsCableOuterFECScheme
-gst_mpegts_descriptor_parse_cable_delivery_system
-<SUBSECTION terrestrial_delivery>
-GstMpegtsTerrestrialDeliverySystemDescriptor
-GstMpegtsTerrestrialTransmissionMode
-GstMpegtsTerrestrialGuardInterval
-GstMpegtsTerrestrialHierarchy
-GstMpegtsModulationType
-GstMpegtsDVBCodeRate
-gst_mpegts_descriptor_parse_terrestrial_delivery_system
-<SUBSECTION t2_delivery>
-GstMpegtsT2DeliverySystemCellExtension
-GstMpegtsT2DeliverySystemCell
-GstMpegtsT2DeliverySystemDescriptor
-gst_mpegts_t2_delivery_system_descriptor_free
-gst_mpegts_descriptor_parse_dvb_t2_delivery_system
-<SUBSECTION short_event>
-gst_mpegts_descriptor_parse_dvb_short_event
-<SUBSECTION network_name>
-gst_mpegts_descriptor_parse_dvb_network_name
-gst_mpegts_descriptor_from_dvb_network_name
-<SUBSECTION service>
-GstMpegtsDVBServiceType
-gst_mpegts_descriptor_parse_dvb_service
-gst_mpegts_descriptor_from_dvb_service
-<SUBSECTION teletext>
-GstMpegtsDVBTeletextType
-gst_mpegts_descriptor_parse_dvb_teletext_idx
-gst_mpegts_descriptor_parse_dvb_teletext_nb
-<SUBSECTION subtitling>
-gst_mpegts_descriptor_parse_dvb_subtitling_idx
-gst_mpegts_descriptor_parse_dvb_subtitling_nb
-gst_mpegts_descriptor_from_dvb_subtitling
-<SUBSECTION linkage>
-GstMpegtsDVBLinkageType
-GstMpegtsDVBLinkageHandOverType
-GstMpegtsDVBLinkageMobileHandOver
-GstMpegtsDVBLinkageEvent
-GstMpegtsDVBLinkageExtendedEvent
-GstMpegtsDVBLinkageDescriptor
-gst_mpegts_dvb_linkage_descriptor_free
-gst_mpegts_dvb_linkage_descriptor_get_mobile_hand_over
-gst_mpegts_dvb_linkage_descriptor_get_event
-gst_mpegts_dvb_linkage_descriptor_get_extended_event
-gst_mpegts_descriptor_parse_dvb_linkage
-<SUBSECTION private_data_specifier>
-gst_mpegts_descriptor_parse_dvb_private_data_specifier
-<SUBSECTION frequency_list>
-gst_mpegts_descriptor_parse_dvb_frequency_list
-<SUBSECTION data_broadcast>
-GstMpegtsDataBroadcastDescriptor
-gst_mpegts_dvb_data_broadcast_descriptor_free
-gst_mpegts_descriptor_parse_dvb_data_broadcast
-<SUBSECTION scrambling>
-GstMpegtsDVBScramblingModeType
-gst_mpegts_descriptor_parse_dvb_scrambling
-<SUBSECTION data_broadcast_id>
-gst_mpegts_descriptor_parse_dvb_data_broadcast_id
-<SUBSECTION parental_rating>
-GstMpegtsDVBParentalRatingItem
-gst_mpegts_descriptor_parse_dvb_parental_rating
-<SUBSECTION stream_identifier>
-gst_mpegts_descriptor_parse_dvb_stream_identifier
-<SUBSECTION ca_identifier>
-gst_mpegts_descriptor_parse_dvb_ca_identifier
-<SUBSECTION service_list>
-GstMpegtsDVBServiceListItem
-gst_mpegts_descriptor_parse_dvb_service_list
-<SUBSECTION stuffing>
-gst_mpegts_descriptor_parse_dvb_stuffing
-<SUBSECTION bouquet_name>
-gst_mpegts_descriptor_parse_dvb_bouquet_name
-<SUBSECTION multilingual_network_name>
-GstMpegtsDvbMultilingualNetworkNameItem
-gst_mpegts_descriptor_parse_dvb_multilingual_network_name
-<SUBSECTION multilingual_bouquet_name>
-GstMpegtsDvbMultilingualBouquetNameItem
-gst_mpegts_descriptor_parse_dvb_multilingual_bouquet_name
-<SUBSECTION multilingual_service_name>
-GstMpegtsDvbMultilingualServiceNameItem
-gst_mpegts_descriptor_parse_dvb_multilingual_service_name
-<SUBSECTION multilingual_component>
-GstMpegtsDvbMultilingualComponentItem
-gst_mpegts_descriptor_parse_dvb_multilingual_component
-<SUBSECTION Standard>
-GST_TYPE_MPEGTS_DVB_CODE_RATE
-GST_TYPE_MPEGTS_COMPONENT_DESCRIPTOR
-GST_TYPE_MPEGTS_DVB_DATA_BROADCAST_DESCRIPTOR
-GST_TYPE_MPEGTS_DVB_LINKAGE_DESCRIPTOR
-GST_TYPE_MPEGTS_EXTENDED_EVENT_DESCRIPTOR
-GST_TYPE_MPEGTS_T2_DELIVERY_SYSTEM_DESCRIPTOR
-gst_mpegts_dvb_code_rate_get_type
-gst_mpegts_component_descriptor_get_type
-gst_mpegts_dvb_data_broadcast_descriptor_get_type
-gst_mpegts_dvb_linkage_descriptor_get_type
-gst_mpegts_extended_event_descriptor_get_type
-gst_mpegts_t2_delivery_system_descriptor_get_type
-</SECTION>
-
-<SECTION>
-<FILE>gst-isdb-descriptor</FILE>
-GstMpegtsISDBDescriptorType
-<SUBSECTION Standard>
-GST_TYPE_MPEGTS_ISDB_DESCRIPTOR_TYPE
-gst_mpegts_isdb_descriptor_type_get_type
-</SECTION>
-
-<SECTION>
-<FILE>gstmpegtssection</FILE>
-<SUBSECTION section>
-GST_MPEGTS_SECTION_TYPE
-GstMpegtsSection
-GstMpegtsSectionTableID
-GstMpegtsSectionType
-gst_message_new_mpegts_section
-gst_message_parse_mpegts_section
-gst_mpegts_section_send_event
-gst_event_parse_mpegts_section
-gst_mpegts_section_packetize
-gst_mpegts_section_new
-gst_mpegts_section_ref
-gst_mpegts_section_unref
-<SUBSECTION PAT>
-GstMpegtsPatProgram
-gst_mpegts_section_get_pat
-gst_mpegts_pat_new
-gst_mpegts_pat_program_new
-gst_mpegts_section_from_pat
-<SUBSECTION PMT>
-GstMpegtsPMT
-GstMpegtsPMTStream
-GstMpegtsStreamType
-gst_mpegts_section_get_pmt
-gst_mpegts_pmt_new
-gst_mpegts_pmt_stream_new
-gst_mpegts_section_from_pmt
-<SUBSECTION TSDT>
-gst_mpegts_section_get_tsdt
-<SUBSECTION CAT>
-gst_mpegts_section_get_cat
-<SUBSECTION Standard>
-GST_TYPE_MPEGTS_SECTION_TABLE_ID
-GST_TYPE_MPEGTS_SECTION_TYPE
-GST_TYPE_MPEGTS_SECTION_DVB_TABLE_ID
-GST_MPEGTS_SECTION
-GST_TYPE_MPEGTS_STREAM_TYPE
-GST_TYPE_MPEGTS_PMT
-GST_TYPE_MPEGTS_PMT_STREAM
-GST_TYPE_MPEGTS_SECTION
-gst_mpegts_section_table_id_get_type
-gst_mpegts_section_type_get_type
-gst_mpegts_pmt_get_type
-gst_mpegts_pmt_stream_get_type
-gst_mpegts_section_get_type
-gst_mpegts_stream_type_get_type
-</SECTION>
-
-<SECTION>
-<FILE>gst-atsc-section</FILE>
-GstMpegtsSectionATSCTableID
-GstMpegtsAtscVCTSource
-GstMpegtsAtscVCT
-gst_mpegts_section_get_atsc_tvct
-gst_mpegts_section_get_atsc_cvct
-GstMpegtsAtscMGTTableType
-GstMpegtsAtscMGTTable
-GstMpegtsAtscMGT
-gst_mpegts_section_get_atsc_mgt
-gst_mpegts_atsc_string_segment_get_string
-GstMpegtsAtscMultString
-GstMpegtsAtscEITEvent
-GstMpegtsAtscEIT
-gst_mpegts_section_get_atsc_eit
-GstMpegtsAtscETT
-gst_mpegts_section_get_atsc_ett
-GstMpegtsAtscSTT
-gst_mpegts_section_get_atsc_stt
-gst_mpegts_atsc_stt_get_datetime_utc
-<SUBSECTION Standard>
-GST_TYPE_MPEGTS_ATSC_EIT
-GST_TYPE_MPEGTS_ATSC_EIT_EVENT
-GST_TYPE_MPEGTS_ATSC_ETT
-GST_TYPE_MPEGTS_ATSC_MGT
-GST_TYPE_MPEGTS_ATSC_MGT_TABLE
-GST_TYPE_MPEGTS_ATSC_MULT_STRING
-GST_TYPE_MPEGTS_ATSC_STRING_SEGMENT
-GST_TYPE_MPEGTS_ATSC_STT
-GST_TYPE_MPEGTS_ATSC_VCT
-GST_TYPE_MPEGTS_ATSC_VCT_SOURCE
-GstMpegtsAtscStringSegment
-gst_mpegts_atsc_eit_event_get_type
-gst_mpegts_atsc_eit_get_type
-gst_mpegts_atsc_ett_get_type
-gst_mpegts_atsc_mgt_get_type
-gst_mpegts_atsc_mgt_table_get_type
-gst_mpegts_atsc_mult_string_get_type
-gst_mpegts_atsc_string_segment_get_type
-gst_mpegts_atsc_stt_get_type
-gst_mpegts_atsc_vct_get_type
-gst_mpegts_atsc_vct_source_get_type
-</SECTION>
-
-<SECTION>
-<FILE>gst-dvb-section</FILE>
-GstMpegtsSectionDVBTableID
-<SUBSECTION NIT>
-GstMpegtsNIT
-GstMpegtsNITStream
-gst_mpegts_section_get_nit
-gst_mpegts_nit_new
-gst_mpegts_nit_stream_new
-gst_mpegts_section_from_nit
-<SUBSECTION BAT>
-GstMpegtsBAT
-GstMpegtsBATStream
-gst_mpegts_section_get_bat
-<SUBSECTION SDT>
-GstMpegtsSDT
-GstMpegtsSDTService
-gst_mpegts_section_get_sdt
-gst_mpegts_sdt_new
-gst_mpegts_sdt_service_new
-gst_mpegts_section_from_sdt
-<SUBSECTION EIT>
-GstMpegtsEIT
-GstMpegtsEITEvent
-GstMpegtsRunningStatus
-gst_mpegts_section_get_eit
-<SUBSECTION TDT>
-gst_mpegts_section_get_tdt
-<SUBSECTION TOT>
-GstMpegtsTOT
-gst_mpegts_section_get_tot
-<SUBSECTION Standard>
-GST_TYPE_MPEGTS_BAT
-GST_TYPE_MPEGTS_BAT_STREAM
-GST_TYPE_MPEGTS_EIT
-GST_TYPE_MPEGTS_EIT_EVENT
-GST_TYPE_MPEGTS_NIT
-GST_TYPE_MPEGTS_NIT_STREAM
-GST_TYPE_MPEGTS_SDT
-GST_TYPE_MPEGTS_SDT_SERVICE
-GST_TYPE_MPEGTS_TOT
-GST_TYPE_MPEGTS_SECTION_DVB_TABLE_ID
-GST_TYPE_MPEGTS_RUNNING_STATUS
-gst_mpegts_running_status_get_type
-gst_mpegts_section_dvb_table_id_get_type
-gst_mpegts_bat_stream_get_type
-gst_mpegts_bat_get_type
-gst_mpegts_eit_event_get_type
-gst_mpegts_eit_get_type
-gst_mpegts_nit_get_type
-gst_mpegts_nit_stream_get_type
-gst_mpegts_sdt_get_type
-gst_mpegts_sdt_service_get_type
-gst_mpegts_tot_get_type
-</SECTION>
-
-
-<SECTION>
-<FILE>gstphotography</FILE>
-<TITLE>GstPhotography</TITLE>
-GstPhotography
-GstPhotographyNoiseReduction
-GstPhotographyWhiteBalanceMode
-GstPhotographyColorToneMode
-GstPhotographySceneMode
-GstPhotographyFlashMode
-GstPhotographyFlickerReductionMode
-GstPhotographyFocusMode
-GstPhotographyFocusStatus
-GstPhotographyCaps
-GstPhotographyShakeRisk
-GstPhotographySettings
-GstPhotographyCapturePrepared
-GST_PHOTOGRAPHY_AUTOFOCUS_DONE
-GST_PHOTOGRAPHY_SHAKE_RISK
-GST_PHOTOGRAPHY_PROP_WB_MODE
-GST_PHOTOGRAPHY_PROP_COLOUR_TONE
-GST_PHOTOGRAPHY_PROP_SCENE_MODE
-GST_PHOTOGRAPHY_PROP_FLASH_MODE
-GST_PHOTOGRAPHY_PROP_NOISE_REDUCTION
-GST_PHOTOGRAPHY_PROP_FOCUS_STATUS
-GST_PHOTOGRAPHY_PROP_CAPABILITIES
-GST_PHOTOGRAPHY_PROP_SHAKE_RISK
-GST_PHOTOGRAPHY_PROP_EV_COMP
-GST_PHOTOGRAPHY_PROP_ISO_SPEED
-GST_PHOTOGRAPHY_PROP_APERTURE
-GST_PHOTOGRAPHY_PROP_EXPOSURE
-GST_PHOTOGRAPHY_PROP_IMAGE_CAPTURE_SUPPORTED_CAPS
-GST_PHOTOGRAPHY_PROP_IMAGE_PREVIEW_SUPPORTED_CAPS
-GST_PHOTOGRAPHY_PROP_FLICKER_MODE
-GST_PHOTOGRAPHY_PROP_FOCUS_MODE
-GST_PHOTOGRAPHY_PROP_ZOOM
-gst_photography_get_ev_compensation
-gst_photography_get_iso_speed
-gst_photography_get_aperture
-gst_photography_get_exposure
-gst_photography_get_white_balance_mode
-gst_photography_get_color_tone_mode
-gst_photography_get_scene_mode
-gst_photography_get_flash_mode
-gst_photography_get_flicker_mode
-gst_photography_get_focus_mode
-gst_photography_get_noise_reduction
-gst_photography_get_zoom
-gst_photography_set_ev_compensation
-gst_photography_set_iso_speed
-gst_photography_set_aperture
-gst_photography_set_exposure
-gst_photography_set_white_balance_mode
-gst_photography_set_color_tone_mode
-gst_photography_set_scene_mode
-gst_photography_set_flash_mode
-gst_photography_set_flicker_mode
-gst_photography_set_focus_mode
-gst_photography_set_noise_reduction
-gst_photography_set_zoom
-gst_photography_get_capabilities
-gst_photography_prepare_for_capture
-gst_photography_set_autofocus
-gst_photography_set_config
-gst_photography_get_config
-<SUBSECTION Standard>
-GST_PHOTOGRAPHY
-GST_IS_PHOTOGRAPHY
-GST_TYPE_PHOTOGRAPHY
-gst_photography_get_type
-GST_PHOTOGRAPHY_GET_INTERFACE
-</SECTION>
-
-<SECTION>
-<FILE>gstbasecamerasrc</FILE>
-<TITLE>GstBaseCameraSrc</TITLE>
-GST_BASE_CAMERA_SRC_CAST
-GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME
-GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME
-GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME
-GST_BASE_CAMERA_SRC_PREVIEW_MESSAGE_NAME
-GstBaseCameraSrc
-GstBaseCameraSrcClass
-MIN_ZOOM
-MAX_ZOOM
-ZOOM_1X
-gst_base_camera_src_get_photography
-gst_base_camera_src_get_color_balance
-gst_base_camera_src_set_mode
-gst_base_camera_src_setup_zoom
-gst_base_camera_src_setup_preview
-gst_base_camera_src_finish_capture
-gst_base_camera_src_post_preview
-<SUBSECTION Standard>
-GST_BASE_CAMERA_SRC
-GST_IS_BASE_CAMERA_SRC
-GST_TYPE_BASE_CAMERA_SRC
-gst_base_camera_src_get_type
-GST_BASE_CAMERA_SRC_CLASS
-GST_IS_BASE_CAMERA_SRC_CLASS
-GST_BASE_CAMERA_SRC_GET_CLASS
-</SECTION>
-
-<SECTION>
-<FILE>gstsignalprocessor</FILE>
-<TITLE>GstSignalProcessor</TITLE>
-GstSignalProcessorClassFlags
-GST_SIGNAL_PROCESSOR_CLASS_CAN_PROCESS_IN_PLACE
-GST_SIGNAL_PROCESSOR_CLASS_SET_CAN_PROCESS_IN_PLACE
-GstSignalProcessorState
-GST_SIGNAL_PROCESSOR_IS_INITIALIZED
-GST_SIGNAL_PROCESSOR_IS_RUNNING
-GstSignalProcessorGroup
-GstSignalProcessor
-GstSignalProcessorClass
-gst_signal_processor_class_add_pad_template
-<SUBSECTION Standard>
-GST_SIGNAL_PROCESSOR
-GST_IS_SIGNAL_PROCESSOR
-GST_TYPE_SIGNAL_PROCESSOR
-gst_signal_processor_get_type
-GST_SIGNAL_PROCESSOR_CLASS
-GST_IS_SIGNAL_PROCESSOR_CLASS
-GST_SIGNAL_PROCESSOR_GET_CLASS
-</SECTION>
-
-
-<SECTION>
-<FILE>photography-enumtypes</FILE>
-gst_photography_noise_reduction_get_type
-GST_TYPE_PHOTOGRAPHY_NOISE_REDUCTION
-gst_white_balance_mode_get_type
-GST_TYPE_WHITE_BALANCE_MODE
-gst_colour_tone_mode_get_type
-GST_TYPE_COLOUR_TONE_MODE
-gst_scene_mode_get_type
-GST_TYPE_SCENE_MODE
-gst_flash_mode_get_type
-GST_TYPE_FLASH_MODE
-gst_focus_status_get_type
-GST_TYPE_FOCUS_STATUS
-gst_photo_caps_get_type
-GST_TYPE_PHOTO_CAPS
-gst_photo_shake_risk_get_type
-GST_TYPE_PHOTO_SHAKE_RISK
-gst_flicker_reduction_mode_get_type
-GST_TYPE_FLICKER_REDUCTION_MODE
-gst_focus_mode_get_type
-GST_TYPE_FOCUS_MODE
-</SECTION>
-
-<SECTION>
-<FILE>gstcamerabin-enum</FILE>
-DEFAULT_WIDTH
-DEFAULT_HEIGHT
-DEFAULT_CAPTURE_WIDTH
-DEFAULT_CAPTURE_HEIGHT
-DEFAULT_FPS_N
-DEFAULT_FPS_D
-DEFAULT_ZOOM
-GstCameraBinMode
-GST_TYPE_CAMERABIN_MODE
-gst_camerabin_mode_get_type
-</SECTION>
-
-<SECTION>
-<FILE>gstcamerabinpreview</FILE>
-gst_camerabin_create_preview_pipeline
-gst_camerabin_destroy_preview_pipeline
-gst_camerabin_preview_pipeline_post
-gst_camerabin_preview_set_caps
-</SECTION>
-
-<SECTION>
-<FILE>gstinsertbin</FILE>
-<TITLE>GstInsertbin</TITLE>
-GstInsertBin
-GstInsertBinClass
-GstInsertBinCallback
-gst_insert_bin_new
-gst_insert_bin_append
-gst_insert_bin_prepend
-gst_insert_bin_insert_after
-gst_insert_bin_insert_before
-gst_insert_bin_remove
-<SUBSECTION Standard>
-GST_INSERT_BIN
-GST_INSERT_BIN_CLASS
-GST_INSERT_BIN_GET_CLASS
-GST_IS_INSERT_BIN
-GST_IS_INSERT_BIN_CLASS
-GST_TYPE_INSERT_BIN
-gst_insert_bin_get_type
-<SUBSECTION Private>
-GstInsertBinPrivate
-</SECTION>
-
-<SECTION>
-<FILE>gstplayer</FILE>
-GstPlayer
-
-gst_player_new
-
-gst_player_play
-gst_player_pause
-gst_player_stop
-
-gst_player_seek
-
-gst_player_set_uri
-gst_player_get_uri
-
-gst_player_get_duration
-gst_player_get_position
-
-gst_player_set_volume
-gst_player_set_mute
-gst_player_get_volume
-gst_player_get_mute
-
-gst_player_get_pipeline
-
-gst_player_set_config
-gst_player_get_config
-
-GstPlayerState
-gst_player_state_get_name
-
-GST_PLAYER_ERROR
-GstPlayerError
-gst_player_error_get_name
-
-gst_player_get_media_info
-
-gst_player_set_audio_track
-gst_player_set_video_track
-gst_player_set_subtitle_track
-
-gst_player_get_current_audio_track
-gst_player_get_current_video_track
-gst_player_get_current_subtitle_track
-
-gst_player_set_audio_track_enabled
-gst_player_set_video_track_enabled
-gst_player_set_subtitle_track_enabled
-
-gst_player_set_subtitle_uri
-gst_player_get_subtitle_uri
-
-gst_player_set_visualization
-gst_player_set_visualization_enabled
-gst_player_get_current_visualization
-
-GstPlayerColorBalanceType
-gst_player_color_balance_type_get_name
-
-gst_player_has_color_balance
-gst_player_set_color_balance
-gst_player_get_color_balance
-
-gst_player_get_multiview_mode
-gst_player_set_multiview_mode
-gst_player_get_multiview_flags
-gst_player_set_multiview_flags
-
-gst_player_get_audio_video_offset
-gst_player_set_audio_video_offset
-
-gst_player_get_subtitle_video_offset
-gst_player_set_subtitle_video_offset
-
-gst_player_get_rate
-gst_player_set_rate
-
-GstPlayerSnapshotFormat
-gst_player_get_video_snapshot
-
-GstPlayerSignalDispatcher
-GstPlayerSignalDispatcherInterface
-
-GstPlayerVideoRenderer
-GstPlayerVideoRendererInterface
-
-<SUBSECTION config>
-gst_player_config_set_position_update_interval
-gst_player_config_get_position_update_interval
-
-gst_player_config_set_user_agent
-gst_player_config_get_user_agent
-
-gst_player_config_set_seek_accurate
-gst_player_config_get_seek_accurate
-
-<SUBSECTION Standard>
-GST_IS_PLAYER
-GST_IS_PLAYER_CLASS
-GST_PLAYER
-GST_PLAYER_CAST
-GST_PLAYER_CLASS
-GST_PLAYER_GET_CLASS
-GST_TYPE_PLAYER
-GstPlayerClass
-gst_player_get_type
-
-gst_player_visualization_get_type
-
-GST_TYPE_PLAYER_ERROR
-gst_player_error_quark
-gst_player_error_get_type
-
-GST_TYPE_PLAYER_STATE
-gst_player_state_get_type
-
-GST_TYPE_PLAYER_COLOR_BALANCE_TYPE
-gst_player_color_balance_type_get_type
-
-GST_TYPE_PLAYER_SIGNAL_DISPATCHER
-GST_PLAYER_SIGNAL_DISPATCHER
-GST_IS_PLAYER_SIGNAL_DISPATCHER
-GST_PLAYER_SIGNAL_DISPATCHER_GET_INTERFACE
-gst_player_signal_dispatcher_get_type
-
-GST_TYPE_PLAYER_VIDEO_RENDERER
-GST_IS_PLAYER_VIDEO_RENDERER
-GST_PLAYER_VIDEO_RENDERER
-GST_PLAYER_VIDEO_RENDERER_GET_INTERFACE
-gst_player_video_renderer_get_type
-</SECTION>
-
-<SECTION>
-<FILE>gstplayer-gmaincontextsignaldispatcher</FILE>
-gst_player_g_main_context_signal_dispatcher_new
-
-<SUBSECTION Standard>
-GST_PLAYER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER
-GST_PLAYER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_CAST
-GST_PLAYER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_CLASS
-GST_PLAYER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_GET_CLASS
-GST_IS_PLAYER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER
-GST_IS_PLAYER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_CLASS
-GST_TYPE_PLAYER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER
-
-gst_player_g_main_context_signal_dispatcher_get_type
-</SECTION>
-
-<SECTION>
-<FILE>gstplayer-videooverlayvideorenderer</FILE>
-GstPlayerVideoOverlayVideoRenderer
-
-gst_player_video_overlay_video_renderer_new
-gst_player_video_overlay_video_renderer_new_with_sink
-gst_player_video_overlay_video_renderer_get_window_handle
-gst_player_video_overlay_video_renderer_set_window_handle
-
-gst_player_video_overlay_video_renderer_expose
-
-gst_player_video_overlay_video_renderer_get_render_rectangle
-gst_player_video_overlay_video_renderer_set_render_rectangle
-
-<SUBSECTION Standard>
-GST_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER
-GST_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER_CAST
-GST_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER_CLASS
-GST_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER_GET_CLASS
-GST_IS_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER
-GST_IS_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER_CLASS
-GST_TYPE_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER
-
-gst_player_video_overlay_video_renderer_get_type
-
-</SECTION>
-
-<SECTION>
-<FILE>gstplayer-visualization</FILE>
-GstPlayerVisualization
-gst_player_visualizations_get
-gst_player_visualizations_free
-gst_player_visualization_copy
-gst_player_visualization_free
-
-<SUBSECTION Standard>
-gst_player_visualization_get_type
-</SECTION>
-
-<SECTION>
-<FILE>gstplayer-mediainfo</FILE>
-GstPlayerMediaInfo
-
-GstPlayerStreamInfo
-GstPlayerAudioInfo
-GstPlayerVideoInfo
-GstPlayerSubtitleInfo
-
-gst_player_media_info_get_uri
-gst_player_media_info_get_duration
-gst_player_media_info_get_title
-gst_player_media_info_get_container_format
-gst_player_media_info_is_seekable
-gst_player_media_info_is_live
-gst_player_media_info_get_image_sample
-gst_player_media_info_get_tags
-gst_player_media_info_get_stream_list
-gst_player_media_info_get_number_of_streams
-
-gst_player_media_info_get_audio_streams
-gst_player_media_info_get_number_of_audio_streams
-gst_player_media_info_get_video_streams
-gst_player_media_info_get_number_of_video_streams
-gst_player_media_info_get_subtitle_streams
-gst_player_media_info_get_number_of_subtitle_streams
-
-gst_player_stream_info_get_index
-gst_player_stream_info_get_caps
-gst_player_stream_info_get_tags
-gst_player_stream_info_get_codec
-gst_player_stream_info_get_stream_type
-
-gst_player_audio_info_get_bitrate
-gst_player_audio_info_get_channels
-gst_player_audio_info_get_language
-gst_player_audio_info_get_max_bitrate
-gst_player_audio_info_get_sample_rate
-
-gst_player_video_info_get_bitrate
-gst_player_video_info_get_height
-gst_player_video_info_get_width
-gst_player_video_info_get_framerate
-gst_player_video_info_get_max_bitrate
-gst_player_video_info_get_pixel_aspect_ratio
-
-gst_player_subtitle_info_get_language
-<SUBSECTION Standard>
-GST_PLAYER_MEDIA_INFO
-GST_IS_PLAYER_MEDIA_INFO
-GST_PLAYER_MEDIA_INFO_CLASS
-GST_IS_PLAYER_MEDIA_INFO_CLASS
-GST_TYPE_PLAYER_MEDIA_INFO
-GstPlayerMediaInfoClass
-gst_player_media_info_get_type
-
-GST_PLAYER_STREAM_INFO
-GST_IS_PLAYER_STREAM_INFO
-GST_PLAYER_STREAM_INFO_CLASS
-GST_IS_PLAYER_STREAM_INFO_CLASS
-GST_TYPE_PLAYER_STREAM_INFO
-GstPlayerStreamInfoClass
-gst_player_stream_info_get_type
-
-GST_PLAYER_AUDIO_INFO
-GST_IS_PLAYER_AUDIO_INFO
-GST_PLAYER_AUDIO_INFO_CLASS
-GST_IS_PLAYER_AUDIO_INFO_CLASS
-GST_TYPE_PLAYER_AUDIO_INFO
-GstPlayerAudioInfoClass
-gst_player_audio_info_get_type
-
-GST_PLAYER_VIDEO_INFO
-GST_IS_PLAYER_VIDEO_INFO
-GST_PLAYER_VIDEO_INFO_CLASS
-GST_IS_PLAYER_VIDEO_INFO_CLASS
-GST_TYPE_PLAYER_VIDEO_INFO
-GstPlayerVideoInfoClass
-gst_player_video_info_get_type
-
-GST_IS_PLAYER_SUBTITLE_INFO
-GST_PLAYER_SUBTITLE_INFO
-GST_PLAYER_SUBTITLE_INFO_CLASS
-GST_IS_PLAYER_SUBTITLE_INFO_CLASS
-GST_TYPE_PLAYER_SUBTITLE_INFO
-GstPlayerSubtitleInfoClass
-gst_player_subtitle_info_get_type
-</SECTION>
-
-
-<SECTION>
-<FILE>gstwebrtc-dtlstransport</FILE>
-GstWebRTCDTLSTransportState
-
-gst_webrtc_dtls_transport_new
-
-<SUBSECTION Standard>
-GST_TYPE_WEBRTC_DTLS_TRANSPORT
-gst_webrtc_dtls_transport_get_type
-GstWebRTCDTLSTransport
-GST_WEBRTC_DTLS_TRANSPORT
-GST_IS_WEBRTC_DTLS_TRANSPORT
-GstWebRTCDTLSTransportClass
-GST_WEBRTC_DTLS_TRANSPORT_CLASS
-GST_WEBRTC_DTLS_TRANSPORT_GET_CLASS
-GST_IS_WEBRTC_DTLS_TRANSPORT_CLASS
-</SECTION>
-
-<SECTION>
-<FILE>gstwebrtc-icetransport</FILE>
-GstWebRTCIceRole
-GstWebRTCICEConnectionState
-GstWebRTCICEGatheringState
-
-
-
-<SUBSECTION Standard>
-GST_TYPE_WEBRTC_ICE_TRANSPORT
-gst_webrtc_ice_transport_get_type
-GstWebRTCICETransport
-GST_WEBRTC_ICE_TRANSPORT
-GST_IS_WEBRTC_ICE_TRANSPORT
-GstWebRTCICETransportClass
-GST_WEBRTC_ICE_TRANSPORT_CLASS
-GST_WEBRTC_ICE_TRANSPORT_GET_CLASS
-GST_IS_WEBRTC_ICE_TRANSPORT_CLASS
-</SECTION>
-
-<SECTION>
-<FILE>gstwebrtc-receiver</FILE>
-gst_webrtc_rtp_receiver_new
-gst_webrtc_rtp_receiver_get_parameters
-gst_webrtc_rtp_receiver_set_parameters
-gst_webrtc_rtp_receiver_set_rtcp_transport
-gst_webrtc_rtp_receiver_set_transport
-<SUBSECTION Standard>
-GST_TYPE_WEBRTC_RTP_RECEIVER
-gst_webrtc_rtp_receiver_get_type
-GstWebRTCRTPReceiver
-GST_WEBRTC_RTP_RECEIVER
-GST_IS_WEBRTC_RTP_RECEIVER
-GstWebRTCRTPReceiverClass
-GST_WEBRTC_RTP_RECEIVER_CLASS
-GST_WEBRTC_RTP_RECEIVER_GET_CLASS
-GST_IS_WEBRTC_RTP_RECEIVER_CLASS
-</SECTION>
-
-<SECTION>
-<FILE>gstwebrtc-sender</FILE>
-gst_webrtc_rtp_sender_new
-gst_webrtc_rtp_sender_get_parameters
-gst_webrtc_rtp_sender_set_parameters
-gst_webrtc_rtp_sender_set_rtcp_transport
-gst_webrtc_rtp_sender_set_transport
-<SUBSECTION Standard>
-GST_TYPE_WEBRTC_RTP_SENDER
-gst_webrtc_rtp_sender_get_type
-GstWebRTCRTPSender
-GST_WEBRTC_RTP_SENDER
-GST_IS_WEBRTC_RTP_SENDER
-GstWebRTCRTPSenderClass
-GST_WEBRTC_RTP_SENDER_CLASS
-GST_WEBRTC_RTP_SENDER_GET_CLASS
-GST_IS_WEBRTC_RTP_SENDER_CLASS
-</SECTION>
-
-<SECTION>
-<FILE>gstwebrtc-sessiondescription</FILE>
-GstWebRTCSessionDescription
-gst_webrtc_session_description_new
-gst_webrtc_session_description_copy
-gst_webrtc_session_description_free
-<SUBSECTION Standard>
-gst_webrtc_session_description_get_type
-GST_TYPE_WEBRTC_SESSION_DESCRIPTION
-</SECTION>
-
-<SECTION>
-<FILE>gstwebrtc-transceiver</FILE>
-<SUBSECTION Standard>
-GST_TYPE_WEBRTC_RTP_TRANSCEIVER
-gst_webrtc_rtp_transceiver_get_type
-GstWebRTCRTPTransceiver
-GST_WEBRTC_RTP_TRANSCEIVER
-GST_IS_WEBRTC_RTP_TRANSCEIVER
-GstWebRTCRTPTransceiverClass
-GST_WEBRTC_RTP_TRANSCEIVER_CLASS
-GST_WEBRTC_RTP_TRANSCEIVER_GET_CLASS
-GST_IS_WEBRTC_RTP_TRANSCEIVER_CLASS
-</SECTION>
diff --git a/docs/libs/transcoder/index.md b/docs/libs/transcoder/index.md
new file mode 100644
index 000000000..e6c4e6b0f
--- /dev/null
+++ b/docs/libs/transcoder/index.md
@@ -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*
diff --git a/docs/libs/transcoder/sitemap.txt b/docs/libs/transcoder/sitemap.txt
new file mode 100644
index 000000000..4f91fcd8a
--- /dev/null
+++ b/docs/libs/transcoder/sitemap.txt
@@ -0,0 +1 @@
+gi-index
diff --git a/docs/meson.build b/docs/meson.build
index b3d14d7e1..a4d0cc08d 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -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
diff --git a/gst-libs/gst/meson.build b/gst-libs/gst/meson.build
index daa1bda18..3ee9ad8d3 100644
--- a/gst-libs/gst/meson.build
+++ b/gst-libs/gst/meson.build
@@ -11,6 +11,7 @@ subdir('mpegts')
subdir('opencv')
subdir('player')
subdir('sctp')
+subdir('transcoder')
subdir('vulkan')
subdir('wayland')
subdir('webrtc')
diff --git a/gst-libs/gst/transcoder/gsttranscoder.c b/gst-libs/gst/transcoder/gsttranscoder.c
new file mode 100644
index 000000000..7abe3fa44
--- /dev/null
+++ b/gst-libs/gst/transcoder/gsttranscoder.c
@@ -0,0 +1,1609 @@
+/* GStreamer
+ *
+ * Copyright (C) 2014-2015 Sebastian Dröge <sebastian@centricular.com>
+ * 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.
+ */
+
+/**
+ * SECTION:gsttranscoder
+ * @short_description: High level API to transcode media files
+ * from one format to any other format using the GStreamer framework.
+ * @symbols:
+ * - gst_transcoder_error_quark
+ */
+
+#include "gsttranscoder.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_transcoder_debug);
+#define GST_CAT_DEFAULT gst_transcoder_debug
+
+#define DEFAULT_URI NULL
+#define DEFAULT_POSITION GST_CLOCK_TIME_NONE
+#define DEFAULT_DURATION GST_CLOCK_TIME_NONE
+#define DEFAULT_POSITION_UPDATE_INTERVAL_MS 100
+#define DEFAULT_AVOID_REENCODING FALSE
+
+GQuark
+gst_transcoder_error_quark (void)
+{
+ static GQuark quark;
+
+ if (!quark)
+ quark = g_quark_from_static_string ("gst-transcoder-error-quark");
+
+ return quark;
+}
+
+enum
+{
+ PROP_0,
+ PROP_SIGNAL_DISPATCHER,
+ PROP_SRC_URI,
+ PROP_DEST_URI,
+ PROP_PROFILE,
+ PROP_POSITION,
+ PROP_DURATION,
+ PROP_PIPELINE,
+ PROP_POSITION_UPDATE_INTERVAL,
+ PROP_AVOID_REENCODING,
+ PROP_LAST
+};
+
+enum
+{
+ SIGNAL_POSITION_UPDATED,
+ SIGNAL_DURATION_CHANGED,
+ SIGNAL_DONE,
+ SIGNAL_ERROR,
+ SIGNAL_WARNING,
+ SIGNAL_LAST
+};
+
+struct _GstTranscoder
+{
+ GstObject parent;
+
+ GstTranscoderSignalDispatcher *signal_dispatcher;
+
+ GstEncodingProfile *profile;
+ gchar *source_uri;
+ gchar *dest_uri;
+
+ GThread *thread;
+ GCond cond;
+ GMainContext *context;
+ GMainLoop *loop;
+
+ GstElement *transcodebin;
+ GstBus *bus;
+ GstState target_state, current_state;
+ gboolean is_live, is_eos;
+ GSource *tick_source, *ready_timeout_source;
+
+ guint position_update_interval_ms;
+ gint wanted_cpu_usage;
+
+ GstClockTime last_duration;
+};
+
+struct _GstTranscoderClass
+{
+ GstObjectClass parent_class;
+};
+
+static void
+gst_transcoder_signal_dispatcher_dispatch (GstTranscoderSignalDispatcher * self,
+ GstTranscoder * transcoder, void (*emitter) (gpointer data), gpointer data,
+ GDestroyNotify destroy);
+
+#define parent_class gst_transcoder_parent_class
+G_DEFINE_TYPE (GstTranscoder, gst_transcoder, GST_TYPE_OBJECT);
+
+static guint signals[SIGNAL_LAST] = { 0, };
+static GParamSpec *param_specs[PROP_LAST] = { NULL, };
+
+static void gst_transcoder_dispose (GObject * object);
+static void gst_transcoder_finalize (GObject * object);
+static void gst_transcoder_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_transcoder_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_transcoder_constructed (GObject * object);
+
+static gpointer gst_transcoder_main (gpointer data);
+
+static gboolean gst_transcoder_set_position_update_interval_internal (gpointer
+ user_data);
+
+
+/**
+ * gst_transcoder_set_cpu_usage:
+ * @self: The GstTranscoder to limit CPU usage on.
+ * @cpu_usage: The percentage of the CPU the process running the transcoder
+ * should try to use. It takes into account the number of cores available.
+ *
+ * Sets @cpu_usage as target percentage CPU usage of the process running the
+ * transcoding task. It will modulate the transcoding speed to reach that target
+ * usage.
+ */
+void
+gst_transcoder_set_cpu_usage (GstTranscoder * self, gint cpu_usage)
+{
+ GST_OBJECT_LOCK (self);
+ self->wanted_cpu_usage = cpu_usage;
+ if (self->transcodebin)
+ g_object_set (self->transcodebin, "cpu-usage", cpu_usage, NULL);
+ GST_OBJECT_UNLOCK (self);
+}
+
+static void
+gst_transcoder_init (GstTranscoder * self)
+{
+ GST_TRACE_OBJECT (self, "Initializing");
+
+ self = gst_transcoder_get_instance_private (self);
+
+ g_cond_init (&self->cond);
+
+ self->context = g_main_context_new ();
+ self->loop = g_main_loop_new (self->context, FALSE);
+ self->wanted_cpu_usage = 100;
+
+ self->position_update_interval_ms = DEFAULT_POSITION_UPDATE_INTERVAL_MS;
+
+ GST_TRACE_OBJECT (self, "Initialized");
+}
+
+static void
+gst_transcoder_class_init (GstTranscoderClass * klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->set_property = gst_transcoder_set_property;
+ gobject_class->get_property = gst_transcoder_get_property;
+ gobject_class->dispose = gst_transcoder_dispose;
+ gobject_class->finalize = gst_transcoder_finalize;
+ gobject_class->constructed = gst_transcoder_constructed;
+
+ param_specs[PROP_SIGNAL_DISPATCHER] =
+ g_param_spec_object ("signal-dispatcher",
+ "Signal Dispatcher", "Dispatcher for the signals to e.g. event loops",
+ GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ param_specs[PROP_SRC_URI] =
+ g_param_spec_string ("src-uri", "URI", "Source URI", DEFAULT_URI,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ param_specs[PROP_DEST_URI] =
+ g_param_spec_string ("dest-uri", "URI", "Source URI", DEFAULT_URI,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ param_specs[PROP_PROFILE] =
+ g_param_spec_object ("profile", "Profile",
+ "The GstEncodingProfile to use", GST_TYPE_ENCODING_PROFILE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ param_specs[PROP_POSITION] =
+ g_param_spec_uint64 ("position", "Position", "Current Position",
+ 0, G_MAXUINT64, DEFAULT_POSITION,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ param_specs[PROP_DURATION] =
+ g_param_spec_uint64 ("duration", "Duration", "Duration",
+ 0, G_MAXUINT64, DEFAULT_DURATION,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ param_specs[PROP_PIPELINE] =
+ g_param_spec_object ("pipeline", "Pipeline",
+ "GStreamer pipeline that is used",
+ GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+ param_specs[PROP_POSITION_UPDATE_INTERVAL] =
+ g_param_spec_uint ("position-update-interval", "Position update interval",
+ "Interval in milliseconds between two position-updated signals."
+ "Pass 0 to stop updating the position.",
+ 0, 10000, DEFAULT_POSITION_UPDATE_INTERVAL_MS,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GstTranscoder:avoid-reencoding:
+ *
+ * See #encodebin:avoid-reencoding
+ */
+ param_specs[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_properties (gobject_class, PROP_LAST, param_specs);
+
+ signals[SIGNAL_POSITION_UPDATED] =
+ g_signal_new ("position-updated", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
+ NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME);
+
+ signals[SIGNAL_DURATION_CHANGED] =
+ g_signal_new ("duration-changed", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
+ NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME);
+
+ signals[SIGNAL_DONE] =
+ g_signal_new ("done", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
+ NULL, NULL, G_TYPE_NONE, 0, G_TYPE_INVALID);
+
+ signals[SIGNAL_ERROR] =
+ g_signal_new ("error", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
+ NULL, NULL, G_TYPE_NONE, 2, G_TYPE_ERROR, GST_TYPE_STRUCTURE);
+
+ signals[SIGNAL_WARNING] =
+ g_signal_new ("warning", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
+ NULL, NULL, G_TYPE_NONE, 2, G_TYPE_ERROR, GST_TYPE_STRUCTURE);
+}
+
+static void
+gst_transcoder_dispose (GObject * object)
+{
+ GstTranscoder *self = GST_TRANSCODER (object);
+
+ GST_TRACE_OBJECT (self, "Stopping main thread");
+
+ if (self->loop) {
+ g_main_loop_quit (self->loop);
+
+ g_thread_join (self->thread);
+ self->thread = NULL;
+
+ g_main_loop_unref (self->loop);
+ self->loop = NULL;
+
+ g_main_context_unref (self->context);
+ self->context = NULL;
+
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_transcoder_finalize (GObject * object)
+{
+ GstTranscoder *self = GST_TRANSCODER (object);
+
+ GST_TRACE_OBJECT (self, "Finalizing");
+
+ g_free (self->source_uri);
+ g_free (self->dest_uri);
+ if (self->signal_dispatcher)
+ g_object_unref (self->signal_dispatcher);
+ g_cond_clear (&self->cond);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_transcoder_constructed (GObject * object)
+{
+ GstTranscoder *self = GST_TRANSCODER (object);
+
+ GST_TRACE_OBJECT (self, "Constructed");
+
+ self->transcodebin =
+ gst_element_factory_make ("uritranscodebin", "uritranscodebin");
+
+ g_object_set (self->transcodebin, "source-uri", self->source_uri,
+ "dest-uri", self->dest_uri, "profile", self->profile,
+ "cpu-usage", self->wanted_cpu_usage, NULL);
+
+ GST_OBJECT_LOCK (self);
+ self->thread = g_thread_new ("GstTranscoder", gst_transcoder_main, self);
+ while (!self->loop || !g_main_loop_is_running (self->loop))
+ g_cond_wait (&self->cond, GST_OBJECT_GET_LOCK (self));
+ GST_OBJECT_UNLOCK (self);
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+}
+
+static void
+gst_transcoder_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstTranscoder *self = GST_TRANSCODER (object);
+
+ switch (prop_id) {
+ case PROP_SIGNAL_DISPATCHER:
+ self->signal_dispatcher = g_value_dup_object (value);
+ break;
+ case PROP_SRC_URI:{
+ GST_OBJECT_LOCK (self);
+ g_free (self->source_uri);
+ self->source_uri = g_value_dup_string (value);
+ GST_DEBUG_OBJECT (self, "Set source_uri=%s", self->source_uri);
+ 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_DEBUG_OBJECT (self, "Set dest_uri=%s", self->dest_uri);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ }
+ case PROP_POSITION_UPDATE_INTERVAL:
+ GST_OBJECT_LOCK (self);
+ self->position_update_interval_ms = g_value_get_uint (value);
+ GST_DEBUG_OBJECT (self, "Set position update interval=%u ms",
+ g_value_get_uint (value));
+ GST_OBJECT_UNLOCK (self);
+
+ gst_transcoder_set_position_update_interval_internal (self);
+ break;
+ case PROP_PROFILE:
+ GST_OBJECT_LOCK (self);
+ self->profile = g_value_dup_object (value);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_AVOID_REENCODING:
+ g_object_set (self->transcodebin, "avoid-reencoding",
+ g_value_get_boolean (value), NULL);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_transcoder_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstTranscoder *self = GST_TRANSCODER (object);
+
+ switch (prop_id) {
+ case PROP_SRC_URI:
+ GST_OBJECT_LOCK (self);
+ g_value_set_string (value, self->source_uri);
+ 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_POSITION:{
+ gint64 position = 0;
+
+ if (self->is_eos)
+ position = self->last_duration;
+ else
+ gst_element_query_position (self->transcodebin, GST_FORMAT_TIME,
+ &position);
+ g_value_set_uint64 (value, position);
+ GST_TRACE_OBJECT (self, "Returning position=%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (g_value_get_uint64 (value)));
+ break;
+ }
+ case PROP_DURATION:{
+ gint64 duration = 0;
+
+ gst_element_query_duration (self->transcodebin, GST_FORMAT_TIME,
+ &duration);
+ g_value_set_uint64 (value, duration);
+ GST_TRACE_OBJECT (self, "Returning duration=%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (g_value_get_uint64 (value)));
+ break;
+ }
+ case PROP_PIPELINE:
+ g_value_set_object (value, self->transcodebin);
+ break;
+ case PROP_POSITION_UPDATE_INTERVAL:
+ GST_OBJECT_LOCK (self);
+ g_value_set_uint (value,
+ gst_transcoder_get_position_update_interval (self));
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_PROFILE:
+ GST_OBJECT_LOCK (self);
+ g_value_set_object (value, self->profile);
+ GST_OBJECT_UNLOCK (self);
+ break;
+ case PROP_AVOID_REENCODING:
+ {
+ gboolean avoid_reencoding;
+
+ g_object_get (self->transcodebin, "avoid-reencoding", &avoid_reencoding,
+ NULL);
+ g_value_set_boolean (value, avoid_reencoding);
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+main_loop_running_cb (gpointer user_data)
+{
+ GstTranscoder *self = GST_TRANSCODER (user_data);
+
+ GST_TRACE_OBJECT (self, "Main loop running now");
+
+ GST_OBJECT_LOCK (self);
+ g_cond_signal (&self->cond);
+ GST_OBJECT_UNLOCK (self);
+
+ return G_SOURCE_REMOVE;
+}
+
+typedef struct
+{
+ GstTranscoder *transcoder;
+ GstClockTime position;
+} PositionUpdatedSignalData;
+
+static void
+position_updated_dispatch (gpointer user_data)
+{
+ PositionUpdatedSignalData *data = user_data;
+
+ if (data->transcoder->target_state >= GST_STATE_PAUSED) {
+ g_signal_emit (data->transcoder, signals[SIGNAL_POSITION_UPDATED], 0,
+ data->position);
+ g_object_notify_by_pspec (G_OBJECT (data->transcoder),
+ param_specs[PROP_POSITION]);
+ }
+}
+
+static void
+position_updated_signal_data_free (PositionUpdatedSignalData * data)
+{
+ g_object_unref (data->transcoder);
+ g_free (data);
+}
+
+static gboolean
+tick_cb (gpointer user_data)
+{
+ GstTranscoder *self = GST_TRANSCODER (user_data);
+ gint64 position;
+
+ if (self->target_state >= GST_STATE_PAUSED
+ && gst_element_query_position (self->transcodebin, GST_FORMAT_TIME,
+ &position)) {
+ GST_LOG_OBJECT (self, "Position %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (position));
+
+ if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
+ signals[SIGNAL_POSITION_UPDATED], 0, NULL, NULL, NULL) != 0) {
+ PositionUpdatedSignalData *data = g_new0 (PositionUpdatedSignalData, 1);
+
+ data->transcoder = g_object_ref (self);
+ data->position = position;
+ gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self,
+ position_updated_dispatch, data,
+ (GDestroyNotify) position_updated_signal_data_free);
+ }
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+add_tick_source (GstTranscoder * self)
+{
+ if (self->tick_source)
+ return;
+
+ if (!self->position_update_interval_ms)
+ return;
+
+ self->tick_source = g_timeout_source_new (self->position_update_interval_ms);
+ g_source_set_callback (self->tick_source, (GSourceFunc) tick_cb, self, NULL);
+ g_source_attach (self->tick_source, self->context);
+}
+
+static void
+remove_tick_source (GstTranscoder * self)
+{
+ if (!self->tick_source)
+ return;
+
+ g_source_destroy (self->tick_source);
+ g_source_unref (self->tick_source);
+ self->tick_source = NULL;
+}
+
+typedef struct
+{
+ GstTranscoder *transcoder;
+ GError *err;
+ GstStructure *details;
+} IssueSignalData;
+
+static void
+error_dispatch (gpointer user_data)
+{
+ IssueSignalData *data = user_data;
+
+ g_signal_emit (data->transcoder, signals[SIGNAL_ERROR], 0, data->err,
+ data->details);
+}
+
+static void
+free_issue_signal_data (IssueSignalData * data)
+{
+ g_object_unref (data->transcoder);
+ if (data->details)
+ gst_structure_free (data->details);
+ g_clear_error (&data->err);
+ g_free (data);
+}
+
+static void
+emit_error (GstTranscoder * self, GError * err, const GstStructure * details)
+{
+ if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
+ signals[SIGNAL_ERROR], 0, NULL, NULL, NULL) != 0) {
+ IssueSignalData *data = g_new0 (IssueSignalData, 1);
+
+ data->transcoder = g_object_ref (self);
+ data->err = g_error_copy (err);
+ if (details)
+ data->details = gst_structure_copy (details);
+ gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self,
+ error_dispatch, data, (GDestroyNotify) free_issue_signal_data);
+ }
+
+ g_error_free (err);
+
+ remove_tick_source (self);
+
+ self->target_state = GST_STATE_NULL;
+ self->current_state = GST_STATE_NULL;
+ self->is_live = FALSE;
+ self->is_eos = FALSE;
+ gst_element_set_state (self->transcodebin, GST_STATE_NULL);
+}
+
+static void
+dump_dot_file (GstTranscoder * self, const gchar * name)
+{
+ gchar *full_name;
+
+ full_name = g_strdup_printf ("gst-transcoder.%p.%s", self, name);
+
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (self->transcodebin),
+ GST_DEBUG_GRAPH_SHOW_VERBOSE, full_name);
+
+ g_free (full_name);
+}
+
+static void
+warning_dispatch (gpointer user_data)
+{
+ IssueSignalData *data = user_data;
+
+ g_signal_emit (data->transcoder, signals[SIGNAL_WARNING], 0, data->err,
+ data->details);
+}
+
+static void
+emit_warning (GstTranscoder * self, GError * err, const GstStructure * details)
+{
+ if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
+ signals[SIGNAL_WARNING], 0, NULL, NULL, NULL) != 0) {
+ IssueSignalData *data = g_new0 (IssueSignalData, 1);
+
+ data->transcoder = g_object_ref (self);
+ data->err = g_error_copy (err);
+ if (details)
+ data->details = gst_structure_copy (details);
+ gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self,
+ warning_dispatch, data, (GDestroyNotify) free_issue_signal_data);
+ }
+
+ g_error_free (err);
+}
+
+static void
+error_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
+{
+ GError *err;
+ GstTranscoder *self = GST_TRANSCODER (user_data);
+ gchar *name, *debug, *message;
+ GstStructure *details = NULL;
+
+ dump_dot_file (self, "error");
+
+ gst_message_parse_error (msg, &err, &debug);
+ gst_message_parse_error_details (msg, (const GstStructure **) &details);
+
+ if (!details)
+ details = gst_structure_new_empty ("details");
+ else
+ details = gst_structure_copy (details);
+
+ name = gst_object_get_path_string (msg->src);
+ message = gst_error_get_message (err->domain, err->code);
+
+ gst_structure_set (details, "debug", G_TYPE_STRING, debug,
+ "msg-source-element-name", G_TYPE_STRING, "name",
+ "msg-source-type", G_TYPE_GTYPE, G_OBJECT_TYPE (msg->src),
+ "msg-error", G_TYPE_STRING, message, NULL);
+ emit_error (self, g_error_copy (err), details);
+
+ gst_structure_free (details);
+ g_clear_error (&err);
+ g_free (debug);
+ g_free (name);
+ g_free (message);
+}
+
+static void
+warning_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
+{
+ GstTranscoder *self = GST_TRANSCODER (user_data);
+ GError *err, *transcoder_err;
+ gchar *name, *debug, *message, *full_message;
+ const GstStructure *details = NULL;
+
+ dump_dot_file (self, "warning");
+
+ gst_message_parse_warning (msg, &err, &debug);
+ gst_message_parse_warning_details (msg, &details);
+
+ name = gst_object_get_path_string (msg->src);
+ message = gst_error_get_message (err->domain, err->code);
+
+ if (debug)
+ full_message =
+ g_strdup_printf ("Warning from element %s: %s\n%s\n%s", name, message,
+ err->message, debug);
+ else
+ full_message =
+ g_strdup_printf ("Warning from element %s: %s\n%s", name, message,
+ err->message);
+
+ GST_WARNING_OBJECT (self, "WARNING: from element %s: %s\n", name,
+ err->message);
+ if (debug != NULL)
+ GST_WARNING_OBJECT (self, "Additional debug info:\n%s\n", debug);
+
+ transcoder_err =
+ g_error_new_literal (GST_TRANSCODER_ERROR, GST_TRANSCODER_ERROR_FAILED,
+ full_message);
+ emit_warning (self, transcoder_err, details);
+
+ g_clear_error (&err);
+ g_free (debug);
+ g_free (name);
+ g_free (full_message);
+ g_free (message);
+}
+
+static void
+eos_dispatch (gpointer user_data)
+{
+ g_signal_emit (user_data, signals[SIGNAL_DONE], 0);
+}
+
+static void
+eos_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
+ gpointer user_data)
+{
+ GstTranscoder *self = GST_TRANSCODER (user_data);
+
+ GST_DEBUG_OBJECT (self, "End of stream");
+
+ gst_element_query_duration (self->transcodebin, GST_FORMAT_TIME,
+ (gint64 *) & self->last_duration);
+ tick_cb (self);
+ remove_tick_source (self);
+
+ if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
+ signals[SIGNAL_DONE], 0, NULL, NULL, NULL) != 0) {
+ gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self,
+ eos_dispatch, g_object_ref (self), (GDestroyNotify) g_object_unref);
+ }
+ self->is_eos = TRUE;
+}
+
+static void
+clock_lost_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
+ gpointer user_data)
+{
+ GstTranscoder *self = GST_TRANSCODER (user_data);
+ GstStateChangeReturn state_ret;
+
+ GST_DEBUG_OBJECT (self, "Clock lost");
+ if (self->target_state >= GST_STATE_PLAYING) {
+ state_ret = gst_element_set_state (self->transcodebin, GST_STATE_PAUSED);
+ if (state_ret != GST_STATE_CHANGE_FAILURE)
+ state_ret = gst_element_set_state (self->transcodebin, GST_STATE_PLAYING);
+
+ if (state_ret == GST_STATE_CHANGE_FAILURE)
+ emit_error (self, g_error_new (GST_TRANSCODER_ERROR,
+ GST_TRANSCODER_ERROR_FAILED, "Failed to handle clock loss"),
+ NULL);
+ }
+}
+
+typedef struct
+{
+ GstTranscoder *transcoder;
+ GstClockTime duration;
+} DurationChangedSignalData;
+
+static void
+duration_changed_dispatch (gpointer user_data)
+{
+ DurationChangedSignalData *data = user_data;
+
+ if (data->transcoder->target_state >= GST_STATE_PAUSED) {
+ g_signal_emit (data->transcoder, signals[SIGNAL_DURATION_CHANGED], 0,
+ data->duration);
+ g_object_notify_by_pspec (G_OBJECT (data->transcoder),
+ param_specs[PROP_DURATION]);
+ }
+}
+
+static void
+duration_changed_signal_data_free (DurationChangedSignalData * data)
+{
+ g_object_unref (data->transcoder);
+ g_free (data);
+}
+
+static void
+emit_duration_changed (GstTranscoder * self, GstClockTime duration)
+{
+ GST_DEBUG_OBJECT (self, "Duration changed %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (duration));
+
+ if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
+ signals[SIGNAL_DURATION_CHANGED], 0, NULL, NULL, NULL) != 0) {
+ DurationChangedSignalData *data = g_new0 (DurationChangedSignalData, 1);
+
+ data->transcoder = g_object_ref (self);
+ data->duration = duration;
+ gst_transcoder_signal_dispatcher_dispatch (self->signal_dispatcher, self,
+ duration_changed_dispatch, data,
+ (GDestroyNotify) duration_changed_signal_data_free);
+ }
+}
+
+static void
+state_changed_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
+ gpointer user_data)
+{
+ GstTranscoder *self = GST_TRANSCODER (user_data);
+ GstState old_state, new_state, pending_state;
+
+ gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
+
+ if (GST_MESSAGE_SRC (msg) == GST_OBJECT (self->transcodebin)) {
+ gchar *transition_name;
+
+ GST_DEBUG_OBJECT (self, "Changed state old: %s new: %s pending: %s",
+ gst_element_state_get_name (old_state),
+ gst_element_state_get_name (new_state),
+ gst_element_state_get_name (pending_state));
+
+ transition_name = g_strdup_printf ("%s_%s",
+ gst_element_state_get_name (old_state),
+ gst_element_state_get_name (new_state));
+ dump_dot_file (self, transition_name);
+ g_free (transition_name);
+
+ self->current_state = new_state;
+
+ if (new_state == GST_STATE_PLAYING
+ && pending_state == GST_STATE_VOID_PENDING) {
+ add_tick_source (self);
+ }
+ }
+}
+
+static void
+duration_changed_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
+ gpointer user_data)
+{
+ GstTranscoder *self = GST_TRANSCODER (user_data);
+ gint64 duration;
+
+ if (gst_element_query_duration (self->transcodebin, GST_FORMAT_TIME,
+ &duration)) {
+ emit_duration_changed (self, duration);
+ }
+}
+
+static void
+latency_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
+ gpointer user_data)
+{
+ GstTranscoder *self = GST_TRANSCODER (user_data);
+
+ GST_DEBUG_OBJECT (self, "Latency changed");
+
+ gst_bin_recalculate_latency (GST_BIN (self->transcodebin));
+}
+
+static void
+request_state_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
+ gpointer user_data)
+{
+ GstTranscoder *self = GST_TRANSCODER (user_data);
+ GstState state;
+ GstStateChangeReturn state_ret;
+
+ gst_message_parse_request_state (msg, &state);
+
+ GST_DEBUG_OBJECT (self, "State %s requested",
+ gst_element_state_get_name (state));
+
+ self->target_state = state;
+ state_ret = gst_element_set_state (self->transcodebin, state);
+ if (state_ret == GST_STATE_CHANGE_FAILURE)
+ emit_error (self, g_error_new (GST_TRANSCODER_ERROR,
+ GST_TRANSCODER_ERROR_FAILED,
+ "Failed to change to requested state %s",
+ gst_element_state_get_name (state)), NULL);
+}
+
+static void
+element_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
+{
+ GstTranscoder *self = GST_TRANSCODER (user_data);
+ const GstStructure *s;
+
+ s = gst_message_get_structure (msg);
+ if (gst_structure_has_name (s, "redirect")) {
+ const gchar *new_location;
+
+ new_location = gst_structure_get_string (s, "new-location");
+ if (!new_location) {
+ const GValue *locations_list, *location_val;
+ guint i, size;
+
+ locations_list = gst_structure_get_value (s, "locations");
+ size = gst_value_list_get_size (locations_list);
+ for (i = 0; i < size; ++i) {
+ const GstStructure *location_s;
+
+ location_val = gst_value_list_get_value (locations_list, i);
+ if (!GST_VALUE_HOLDS_STRUCTURE (location_val))
+ continue;
+
+ location_s = (const GstStructure *) g_value_get_boxed (location_val);
+ if (!gst_structure_has_name (location_s, "redirect"))
+ continue;
+
+ new_location = gst_structure_get_string (location_s, "new-location");
+ if (new_location)
+ break;
+ }
+ }
+
+ if (new_location) {
+ GST_FIXME_OBJECT (self, "Handle redirection to '%s'", new_location);
+ }
+ }
+}
+
+
+static gpointer
+gst_transcoder_main (gpointer data)
+{
+ GstTranscoder *self = GST_TRANSCODER (data);
+ GstBus *bus;
+ GSource *source;
+ GSource *bus_source;
+
+ GST_TRACE_OBJECT (self, "Starting main thread");
+
+ g_main_context_push_thread_default (self->context);
+
+ source = g_idle_source_new ();
+ g_source_set_callback (source, (GSourceFunc) main_loop_running_cb, self,
+ NULL);
+ g_source_attach (source, self->context);
+ g_source_unref (source);
+
+ self->bus = bus = gst_element_get_bus (self->transcodebin);
+ bus_source = gst_bus_create_watch (bus);
+ g_source_set_callback (bus_source, (GSourceFunc) gst_bus_async_signal_func,
+ NULL, NULL);
+ g_source_attach (bus_source, self->context);
+
+ g_signal_connect (G_OBJECT (bus), "message::error", G_CALLBACK (error_cb),
+ self);
+ g_signal_connect (G_OBJECT (bus), "message::warning", G_CALLBACK (warning_cb),
+ self);
+ g_signal_connect (G_OBJECT (bus), "message::eos", G_CALLBACK (eos_cb), self);
+ g_signal_connect (G_OBJECT (bus), "message::state-changed",
+ G_CALLBACK (state_changed_cb), self);
+ g_signal_connect (G_OBJECT (bus), "message::clock-lost",
+ G_CALLBACK (clock_lost_cb), self);
+ g_signal_connect (G_OBJECT (bus), "message::duration-changed",
+ G_CALLBACK (duration_changed_cb), self);
+ g_signal_connect (G_OBJECT (bus), "message::latency",
+ G_CALLBACK (latency_cb), self);
+ g_signal_connect (G_OBJECT (bus), "message::request-state",
+ G_CALLBACK (request_state_cb), self);
+ g_signal_connect (G_OBJECT (bus), "message::element",
+ G_CALLBACK (element_cb), self);
+
+ self->target_state = GST_STATE_NULL;
+ self->current_state = GST_STATE_NULL;
+ self->is_eos = FALSE;
+ self->is_live = FALSE;
+
+ GST_TRACE_OBJECT (self, "Starting main loop");
+ g_main_loop_run (self->loop);
+ GST_TRACE_OBJECT (self, "Stopped main loop");
+
+ g_source_destroy (bus_source);
+ g_source_unref (bus_source);
+ gst_object_unref (bus);
+
+ remove_tick_source (self);
+
+ g_main_context_pop_thread_default (self->context);
+
+ self->target_state = GST_STATE_NULL;
+ self->current_state = GST_STATE_NULL;
+ if (self->transcodebin) {
+ gst_element_set_state (self->transcodebin, GST_STATE_NULL);
+ g_clear_object (&self->transcodebin);
+ }
+
+ GST_TRACE_OBJECT (self, "Stopped main thread");
+
+ return NULL;
+}
+
+static gpointer
+gst_transcoder_init_once (G_GNUC_UNUSED gpointer user_data)
+{
+ gst_init (NULL, NULL);
+
+ GST_DEBUG_CATEGORY_INIT (gst_transcoder_debug, "gst-transcoder", 0,
+ "GstTranscoder");
+ gst_transcoder_error_quark ();
+
+ return NULL;
+}
+
+static 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;
+}
+
+/**
+ * gst_transcoder_new:
+ * @source_uri: The URI of the media stream to transcode
+ * @dest_uri: The URI of the destination of the transcoded stream
+ * @encoding_profile: The serialized #GstEncodingProfile defining the output
+ * format. Have a look at the #GstEncodingProfile documentation to find more
+ * about the serialization format.
+ *
+ * Returns: a new #GstTranscoder instance
+ */
+GstTranscoder *
+gst_transcoder_new (const gchar * source_uri,
+ const gchar * dest_uri, const gchar * encoding_profile)
+{
+ GstEncodingProfile *profile;
+
+ profile = create_encoding_profile (encoding_profile);
+
+ return gst_transcoder_new_full (source_uri, dest_uri, profile, NULL);
+}
+
+/**
+ * gst_transcoder_new_full:
+ * @source_uri: The URI of the media stream to transcode
+ * @dest_uri: The URI of the destination of the transcoded stream
+ * @profile: The #GstEncodingProfile defining the output format
+ * have a look at the #GstEncodingProfile documentation to find more
+ * about the serialization format.
+ * @signal_dispatcher: The #GstTranscoderSignalDispatcher to be used
+ * to dispatch the various signals.
+ *
+ * Returns: a new #GstTranscoder instance
+ */
+GstTranscoder *
+gst_transcoder_new_full (const gchar * source_uri,
+ const gchar * dest_uri, GstEncodingProfile * profile,
+ GstTranscoderSignalDispatcher * signal_dispatcher)
+{
+ static GOnce once = G_ONCE_INIT;
+
+ g_once (&once, gst_transcoder_init_once, NULL);
+
+ g_return_val_if_fail (source_uri, NULL);
+ g_return_val_if_fail (dest_uri, NULL);
+
+ return g_object_new (GST_TYPE_TRANSCODER, "src-uri", source_uri,
+ "dest-uri", dest_uri, "profile", profile,
+ "signal-dispatcher", signal_dispatcher, NULL);
+}
+
+typedef struct
+{
+ GError **user_error;
+ GMutex m;
+ GCond cond;
+
+ gboolean done;
+
+} RunSyncData;
+
+static void
+_error_cb (GstTranscoder * self, GError * error, GstStructure * details,
+ RunSyncData * data)
+{
+ g_mutex_lock (&data->m);
+ data->done = TRUE;
+ if (data->user_error && (*data->user_error) == NULL)
+ g_propagate_error (data->user_error, error);
+ g_cond_broadcast (&data->cond);
+ g_mutex_unlock (&data->m);
+}
+
+static void
+_done_cb (GstTranscoder * self, RunSyncData * data)
+{
+ g_mutex_lock (&data->m);
+ data->done = TRUE;
+ g_cond_broadcast (&data->cond);
+ g_mutex_unlock (&data->m);
+}
+
+/**
+ * gst_transcoder_run:
+ * @self: The GstTranscoder to run
+ * @error: (allow-none): An error to be set if transcoding fails
+ *
+ * Run the transcoder task synchonously. You can connect
+ * to the 'position' signal to get information about the
+ * progress of the transcoding.
+ */
+gboolean
+gst_transcoder_run (GstTranscoder * self, GError ** error)
+{
+ RunSyncData data = { 0, };
+
+ g_mutex_init (&data.m);
+ g_cond_init (&data.cond);
+
+ g_signal_connect (self, "error", G_CALLBACK (_error_cb), &data);
+ g_signal_connect (self, "done", G_CALLBACK (_done_cb), &data);
+ gst_transcoder_run_async (self);
+
+ g_mutex_lock (&data.m);
+ while (!data.done) {
+ g_cond_wait (&data.cond, &data.m);
+ }
+ g_mutex_unlock (&data.m);
+
+ if (data.user_error) {
+ g_propagate_error (error, *data.user_error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gst_transcoder_run_async:
+ * @self: The GstTranscoder to run
+ *
+ * Run the transcoder task asynchronously. You should connect
+ * to the 'done' signal to be notified about when the
+ * transcoding is done, and to the 'error' signal to be
+ * notified about any error.
+ */
+void
+gst_transcoder_run_async (GstTranscoder * self)
+{
+ GstStateChangeReturn state_ret;
+
+ GST_DEBUG_OBJECT (self, "Play");
+
+ if (!self->profile) {
+ emit_error (self, g_error_new (GST_TRANSCODER_ERROR,
+ GST_TRANSCODER_ERROR_FAILED, "No \"profile\" provided"), NULL);
+
+ return;
+ }
+
+ self->target_state = GST_STATE_PLAYING;
+ state_ret = gst_element_set_state (self->transcodebin, GST_STATE_PLAYING);
+
+ if (state_ret == GST_STATE_CHANGE_FAILURE) {
+ emit_error (self, g_error_new (GST_TRANSCODER_ERROR,
+ GST_TRANSCODER_ERROR_FAILED, "Could not start transcoding"), NULL);
+ return;
+ } else if (state_ret == GST_STATE_CHANGE_NO_PREROLL) {
+ self->is_live = TRUE;
+ GST_DEBUG_OBJECT (self, "Pipeline is live");
+ }
+
+ return;
+}
+
+static gboolean
+gst_transcoder_set_position_update_interval_internal (gpointer user_data)
+{
+ GstTranscoder *self = user_data;
+
+ GST_OBJECT_LOCK (self);
+
+ if (self->tick_source) {
+ remove_tick_source (self);
+ add_tick_source (self);
+ }
+
+ GST_OBJECT_UNLOCK (self);
+
+ return G_SOURCE_REMOVE;
+}
+
+/**
+ * gst_transcoder_set_position_update_interval:
+ * @self: #GstTranscoder instance
+ * @interval: interval in ms
+ *
+ * Set interval in milliseconds between two position-updated signals.
+ * Pass 0 to stop updating the position.
+ */
+void
+gst_transcoder_set_position_update_interval (GstTranscoder * self,
+ guint interval)
+{
+ g_return_if_fail (GST_IS_TRANSCODER (self));
+ g_return_if_fail (interval <= 10000);
+
+ GST_OBJECT_LOCK (self);
+ self->position_update_interval_ms = interval;
+ GST_OBJECT_UNLOCK (self);
+
+ gst_transcoder_set_position_update_interval_internal (self);
+}
+
+/**
+ * gst_transcoder_get_position_update_interval:
+ * @self: #GstTranscoder instance
+ *
+ * Returns: current position update interval in milliseconds
+ */
+guint
+gst_transcoder_get_position_update_interval (GstTranscoder * self)
+{
+ g_return_val_if_fail (GST_IS_TRANSCODER (self),
+ DEFAULT_POSITION_UPDATE_INTERVAL_MS);
+
+ return self->position_update_interval_ms;
+}
+
+/**
+ * gst_transcoder_get_source_uri:
+ * @self: #GstTranscoder instance
+ *
+ * Gets the URI of the currently-transcoding stream.
+ *
+ * Returns: (transfer full): a string containing the URI of the
+ * source stream. g_free() after usage.
+ */
+gchar *
+gst_transcoder_get_source_uri (GstTranscoder * self)
+{
+ gchar *val;
+
+ g_return_val_if_fail (GST_IS_TRANSCODER (self), DEFAULT_URI);
+
+ g_object_get (self, "src-uri", &val, NULL);
+
+ return val;
+}
+
+/**
+ * gst_transcoder_get_dest_uri:
+ * @self: #GstTranscoder instance
+ *
+ * Gets the URI of the destination of the transcoded stream.
+ *
+ * Returns: (transfer full): a string containing the URI of the
+ * destination of the transcoded stream. g_free() after usage.
+ */
+gchar *
+gst_transcoder_get_dest_uri (GstTranscoder * self)
+{
+ gchar *val;
+
+ g_return_val_if_fail (GST_IS_TRANSCODER (self), DEFAULT_URI);
+
+ g_object_get (self, "dest-uri", &val, NULL);
+
+ return val;
+}
+
+/**
+ * gst_transcoder_get_position:
+ * @self: #GstTranscoder instance
+ *
+ * Returns: the absolute position time, in nanoseconds, of the
+ * transcoding stream.
+ */
+GstClockTime
+gst_transcoder_get_position (GstTranscoder * self)
+{
+ GstClockTime val;
+
+ g_return_val_if_fail (GST_IS_TRANSCODER (self), DEFAULT_POSITION);
+
+ g_object_get (self, "position", &val, NULL);
+
+ return val;
+}
+
+/**
+ * gst_transcoder_get_duration:
+ * @self: #GstTranscoder instance
+ *
+ * Retrieves the duration of the media stream that self represents.
+ *
+ * Returns: the duration of the transcoding media stream, in
+ * nanoseconds.
+ */
+GstClockTime
+gst_transcoder_get_duration (GstTranscoder * self)
+{
+ GstClockTime val;
+
+ g_return_val_if_fail (GST_IS_TRANSCODER (self), DEFAULT_DURATION);
+
+ g_object_get (self, "duration", &val, NULL);
+
+ return val;
+}
+
+/**
+ * gst_transcoder_get_pipeline:
+ * @self: #GstTranscoder instance
+ *
+ * Returns: (transfer full): The internal uritranscodebin instance
+ */
+GstElement *
+gst_transcoder_get_pipeline (GstTranscoder * self)
+{
+ GstElement *val;
+
+ g_return_val_if_fail (GST_IS_TRANSCODER (self), NULL);
+
+ g_object_get (self, "pipeline", &val, NULL);
+
+ return val;
+}
+
+/**
+ * gst_transcoder_get_avoid_reencoding:
+ * @self: The #GstTranscoder to check whether reencoding is avoided or not.
+ *
+ * Returns: %TRUE if the transcoder tries to avoid reencoding streams where
+ * reencoding is not strictly needed, %FALSE otherwise.
+ */
+gboolean
+gst_transcoder_get_avoid_reencoding (GstTranscoder * self)
+{
+ gboolean val;
+
+ g_return_val_if_fail (GST_IS_TRANSCODER (self), FALSE);
+
+ g_object_get (self->transcodebin, "avoid-reencoding", &val, NULL);
+
+ return val;
+}
+
+/**
+ * gst_transcoder_set_avoid_reencoding:
+ * @self: The #GstTranscoder to set whether reencoding should be avoided or not.
+ * @avoid_reencoding: %TRUE if the transcoder should try to avoid reencoding
+ * streams where * reencoding is not strictly needed, %FALSE otherwise.
+ */
+void
+gst_transcoder_set_avoid_reencoding (GstTranscoder * self,
+ gboolean avoid_reencoding)
+{
+ g_return_if_fail (GST_IS_TRANSCODER (self));
+
+ g_object_set (self->transcodebin, "avoid-reencoding", avoid_reencoding, NULL);
+}
+
+#define C_ENUM(v) ((gint) v)
+#define C_FLAGS(v) ((guint) v)
+
+GType
+gst_transcoder_error_get_type (void)
+{
+ static gsize id = 0;
+ static const GEnumValue values[] = {
+ {C_ENUM (GST_TRANSCODER_ERROR_FAILED), "GST_TRANSCODER_ERROR_FAILED",
+ "failed"},
+ {0, NULL, NULL}
+ };
+
+ if (g_once_init_enter (&id)) {
+ GType tmp = g_enum_register_static ("GstTranscoderError", values);
+ g_once_init_leave (&id, tmp);
+ }
+
+ return (GType) id;
+}
+
+/**
+ * gst_transcoder_error_get_name:
+ * @error: a #GstTranscoderError
+ *
+ * Gets a string representing the given error.
+ *
+ * Returns: (transfer none): a string with the given error.
+ */
+const gchar *
+gst_transcoder_error_get_name (GstTranscoderError error)
+{
+ switch (error) {
+ case GST_TRANSCODER_ERROR_FAILED:
+ return "failed";
+ }
+
+ g_assert_not_reached ();
+ return NULL;
+}
+
+G_DEFINE_INTERFACE (GstTranscoderSignalDispatcher,
+ gst_transcoder_signal_dispatcher, G_TYPE_OBJECT);
+
+static void
+gst_transcoder_signal_dispatcher_default_init (G_GNUC_UNUSED
+ GstTranscoderSignalDispatcherInterface * iface)
+{
+
+}
+
+static void
+gst_transcoder_signal_dispatcher_dispatch (GstTranscoderSignalDispatcher * self,
+ GstTranscoder * transcoder, void (*emitter) (gpointer data), gpointer data,
+ GDestroyNotify destroy)
+{
+ GstTranscoderSignalDispatcherInterface *iface;
+
+ if (!self) {
+ emitter (data);
+ if (destroy)
+ destroy (data);
+ return;
+ }
+
+ g_return_if_fail (GST_IS_TRANSCODER_SIGNAL_DISPATCHER (self));
+ iface = GST_TRANSCODER_SIGNAL_DISPATCHER_GET_INTERFACE (self);
+ g_return_if_fail (iface->dispatch != NULL);
+
+ iface->dispatch (self, transcoder, emitter, data, destroy);
+}
+
+struct _GstTranscoderGMainContextSignalDispatcher
+{
+ GObject parent;
+ GMainContext *application_context;
+};
+
+struct _GstTranscoderGMainContextSignalDispatcherClass
+{
+ GObjectClass parent_class;
+};
+
+static void
+ gst_transcoder_g_main_context_signal_dispatcher_interface_init
+ (GstTranscoderSignalDispatcherInterface * iface);
+
+enum
+{
+ G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_0,
+ G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT,
+ G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_LAST
+};
+
+G_DEFINE_TYPE_WITH_CODE (GstTranscoderGMainContextSignalDispatcher,
+ gst_transcoder_g_main_context_signal_dispatcher, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GST_TYPE_TRANSCODER_SIGNAL_DISPATCHER,
+ gst_transcoder_g_main_context_signal_dispatcher_interface_init));
+
+static GParamSpec
+ * g_main_context_signal_dispatcher_param_specs
+ [G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_LAST] = { NULL, };
+
+static void
+gst_transcoder_g_main_context_signal_dispatcher_finalize (GObject * object)
+{
+ GstTranscoderGMainContextSignalDispatcher *self =
+ GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (object);
+
+ if (self->application_context)
+ g_main_context_unref (self->application_context);
+
+ G_OBJECT_CLASS
+ (gst_transcoder_g_main_context_signal_dispatcher_parent_class)->finalize
+ (object);
+}
+
+static void
+gst_transcoder_g_main_context_signal_dispatcher_set_property (GObject * object,
+ guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+ GstTranscoderGMainContextSignalDispatcher *self =
+ GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (object);
+
+ switch (prop_id) {
+ case G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT:
+ self->application_context = g_value_dup_boxed (value);
+ if (!self->application_context)
+ self->application_context = g_main_context_ref_thread_default ();
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_transcoder_g_main_context_signal_dispatcher_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec)
+{
+ GstTranscoderGMainContextSignalDispatcher *self =
+ GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (object);
+
+ switch (prop_id) {
+ case G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT:
+ g_value_set_boxed (value, self->application_context);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ gst_transcoder_g_main_context_signal_dispatcher_class_init
+ (GstTranscoderGMainContextSignalDispatcherClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize =
+ gst_transcoder_g_main_context_signal_dispatcher_finalize;
+ gobject_class->set_property =
+ gst_transcoder_g_main_context_signal_dispatcher_set_property;
+ gobject_class->get_property =
+ gst_transcoder_g_main_context_signal_dispatcher_get_property;
+
+ g_main_context_signal_dispatcher_param_specs
+ [G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT] =
+ g_param_spec_boxed ("application-context", "Application Context",
+ "Application GMainContext to dispatch signals to", G_TYPE_MAIN_CONTEXT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class,
+ G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_LAST,
+ g_main_context_signal_dispatcher_param_specs);
+}
+
+static void
+ gst_transcoder_g_main_context_signal_dispatcher_init
+ (G_GNUC_UNUSED GstTranscoderGMainContextSignalDispatcher * self)
+{
+}
+
+typedef struct
+{
+ void (*emitter) (gpointer data);
+ gpointer data;
+ GDestroyNotify destroy;
+} GMainContextSignalDispatcherData;
+
+static gboolean
+g_main_context_signal_dispatcher_dispatch_gsourcefunc (gpointer user_data)
+{
+ GMainContextSignalDispatcherData *data = user_data;
+
+ data->emitter (data->data);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+g_main_context_signal_dispatcher_dispatch_destroy (gpointer user_data)
+{
+ GMainContextSignalDispatcherData *data = user_data;
+
+ if (data->destroy)
+ data->destroy (data->data);
+ g_free (data);
+}
+
+/* *INDENT-OFF* */
+static void
+gst_transcoder_g_main_context_signal_dispatcher_dispatch (GstTranscoderSignalDispatcher * iface,
+ G_GNUC_UNUSED GstTranscoder * transcoder, void (*emitter) (gpointer data),
+ gpointer data, GDestroyNotify destroy)
+{
+ GstTranscoderGMainContextSignalDispatcher *self =
+ GST_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (iface);
+ GMainContextSignalDispatcherData *gsourcefunc_data =
+ g_new0 (GMainContextSignalDispatcherData, 1);
+
+ gsourcefunc_data->emitter = emitter;
+ gsourcefunc_data->data = data;
+ gsourcefunc_data->destroy = destroy;
+
+ g_main_context_invoke_full (self->application_context,
+ G_PRIORITY_DEFAULT, g_main_context_signal_dispatcher_dispatch_gsourcefunc,
+ gsourcefunc_data, g_main_context_signal_dispatcher_dispatch_destroy);
+}
+
+static void
+gst_transcoder_g_main_context_signal_dispatcher_interface_init (GstTranscoderSignalDispatcherInterface * iface)
+{
+ iface->dispatch = gst_transcoder_g_main_context_signal_dispatcher_dispatch;
+}
+/* *INDENT-ON* */
+
+/**
+ * gst_transcoder_g_main_context_signal_dispatcher_new:
+ * @application_context: (allow-none): GMainContext to use or %NULL
+ *
+ * Returns: (transfer full):
+ */
+GstTranscoderSignalDispatcher *
+gst_transcoder_g_main_context_signal_dispatcher_new (GMainContext *
+ application_context)
+{
+ return g_object_new (GST_TYPE_TRANSCODER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER,
+ "application-context", application_context, NULL);
+}
diff --git a/gst-libs/gst/transcoder/gsttranscoder.h b/gst-libs/gst/transcoder/gsttranscoder.h
new file mode 100644
index 000000000..9cc74a420
--- /dev/null
+++ b/gst-libs/gst/transcoder/gsttranscoder.h
@@ -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
diff --git a/gst-libs/gst/transcoder/meson.build b/gst-libs/gst/transcoder/meson.build
new file mode 100644
index 000000000..47076d417
--- /dev/null
+++ b/gst-libs/gst/transcoder/meson.build
@@ -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]
+) \ No newline at end of file
diff --git a/gst-libs/gst/transcoder/transcoder-prelude.h b/gst-libs/gst/transcoder/transcoder-prelude.h
new file mode 100644
index 000000000..ba153dd16
--- /dev/null
+++ b/gst-libs/gst/transcoder/transcoder-prelude.h
@@ -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__ */
diff --git a/gst/meson.build b/gst/meson.build
index 3266c6e20..2197aec95 100644
--- a/gst/meson.build
+++ b/gst/meson.build
@@ -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()
diff --git a/gst/transcode/gst-cpu-throttling-clock.c b/gst/transcode/gst-cpu-throttling-clock.c
new file mode 100644
index 000000000..45bb51a12
--- /dev/null
+++ b/gst/transcode/gst-cpu-throttling-clock.c
@@ -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
diff --git a/gst/transcode/gst-cpu-throttling-clock.h b/gst/transcode/gst-cpu-throttling-clock.h
new file mode 100644
index 000000000..e946c1af2
--- /dev/null
+++ b/gst/transcode/gst-cpu-throttling-clock.h
@@ -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__*/
diff --git a/gst/transcode/gsttranscodebin.c b/gst/transcode/gsttranscodebin.c
new file mode 100644
index 000000000..51812f41d
--- /dev/null
+++ b/gst/transcode/gsttranscodebin.c
@@ -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)
diff --git a/gst/transcode/gsttranscoding.h b/gst/transcode/gsttranscoding.h
new file mode 100644
index 000000000..0e7f29c70
--- /dev/null
+++ b/gst/transcode/gsttranscoding.h
@@ -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__ */
diff --git a/gst/transcode/gsturitranscodebin.c b/gst/transcode/gsturitranscodebin.c
new file mode 100644
index 000000000..9b74ae469
--- /dev/null
+++ b/gst/transcode/gsturitranscodebin.c
@@ -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;
+}
diff --git a/gst/transcode/meson.build b/gst/transcode/meson.build
new file mode 100644
index 000000000..e3814b06e
--- /dev/null
+++ b/gst/transcode/meson.build
@@ -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] \ No newline at end of file
diff --git a/meson.build b/meson.build
index 6a2ba6bd3..67aeeb4d0 100644
--- a/meson.build
+++ b/meson.build
@@ -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)
diff --git a/meson_options.txt b/meson_options.txt
index 607e1e27f..ab9801679 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -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')
diff --git a/pkgconfig/gstreamer-bad-transcoder-uninstalled.pc.in b/pkgconfig/gstreamer-bad-transcoder-uninstalled.pc.in
new file mode 100644
index 000000000..5ae1f82e6
--- /dev/null
+++ b/pkgconfig/gstreamer-bad-transcoder-uninstalled.pc.in
@@ -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
diff --git a/pkgconfig/gstreamer-bad-transcoder.pc.in b/pkgconfig/gstreamer-bad-transcoder.pc.in
new file mode 100644
index 000000000..474bee70f
--- /dev/null
+++ b/pkgconfig/gstreamer-bad-transcoder.pc.in
@@ -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}
diff --git a/pkgconfig/meson.build b/pkgconfig/meson.build
index 5bebe2f97..19ad0b548 100644
--- a/pkgconfig/meson.build
+++ b/pkgconfig/meson.build
@@ -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()))
diff --git a/tools/gst-transcoder.c b/tools/gst-transcoder.c
new file mode 100644
index 000000000..553aea210
--- /dev/null
+++ b/tools/gst-transcoder.c
@@ -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;
+}
diff --git a/tools/meson.build b/tools/meson.build
new file mode 100644
index 000000000..3fda1f637
--- /dev/null
+++ b/tools/meson.build
@@ -0,0 +1,5 @@
+executable('gst-transcoder-' + api_version,
+ 'gst-transcoder.c', 'utils.c',
+ install : true,
+ dependencies : [gst_dep, gstpbutils_dep, gst_transcoder_dep],
+)
diff --git a/tools/utils.c b/tools/utils.c
new file mode 100644
index 000000000..4f855c565
--- /dev/null
+++ b/tools/utils.c
@@ -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, " - ");
+ }
+
+}
diff --git a/tools/utils.h b/tools/utils.h
new file mode 100644
index 000000000..106ec45ec
--- /dev/null
+++ b/tools/utils.h
@@ -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*/