Import GstTranscoder
authorSaunier Thibault <saunierthibault@gmail.com>
Thu, 3 Dec 2015 11:32:05 +0000 (12:32 +0100)
committerThibault Saunier <tsaunier@gnome.org>
Wed, 28 Aug 2019 13:02:13 +0000 (13:02 +0000)
36 files changed:
data/meson.build [new file with mode: 0644]
data/targets/device/dvd.gep [new file with mode: 0644]
data/targets/file-extension/avi.gep [new file with mode: 0644]
data/targets/file-extension/flv.gep [new file with mode: 0644]
data/targets/file-extension/mkv.gep [new file with mode: 0644]
data/targets/file-extension/mp3.gep [new file with mode: 0644]
data/targets/file-extension/mp4.gep [new file with mode: 0644]
data/targets/file-extension/oga.gep [new file with mode: 0644]
data/targets/file-extension/ogv.gep [new file with mode: 0644]
data/targets/file-extension/webm.gep [new file with mode: 0644]
data/targets/online-service/youtube.gep [new file with mode: 0644]
docs/libs/gst-plugins-bad-libs-sections.txt [deleted file]
docs/libs/transcoder/index.md [new file with mode: 0644]
docs/libs/transcoder/sitemap.txt [new file with mode: 0644]
docs/meson.build
gst-libs/gst/meson.build
gst-libs/gst/transcoder/gsttranscoder.c [new file with mode: 0644]
gst-libs/gst/transcoder/gsttranscoder.h [new file with mode: 0644]
gst-libs/gst/transcoder/meson.build [new file with mode: 0644]
gst-libs/gst/transcoder/transcoder-prelude.h [new file with mode: 0644]
gst/meson.build
gst/transcode/gst-cpu-throttling-clock.c [new file with mode: 0644]
gst/transcode/gst-cpu-throttling-clock.h [new file with mode: 0644]
gst/transcode/gsttranscodebin.c [new file with mode: 0644]
gst/transcode/gsttranscoding.h [new file with mode: 0644]
gst/transcode/gsturitranscodebin.c [new file with mode: 0644]
gst/transcode/meson.build [new file with mode: 0644]
meson.build
meson_options.txt
pkgconfig/gstreamer-bad-transcoder-uninstalled.pc.in [new file with mode: 0644]
pkgconfig/gstreamer-bad-transcoder.pc.in [new file with mode: 0644]
pkgconfig/meson.build
tools/gst-transcoder.c [new file with mode: 0644]
tools/meson.build [new file with mode: 0644]
tools/utils.c [new file with mode: 0644]
tools/utils.h [new file with mode: 0644]

diff --git a/data/meson.build b/data/meson.build
new file mode 100644 (file)
index 0000000..4a2d306
--- /dev/null
@@ -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 (file)
index 0000000..d376047
--- /dev/null
@@ -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 (file)
index 0000000..7131ba4
--- /dev/null
@@ -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 (file)
index 0000000..59380ce
--- /dev/null
@@ -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 (file)
index 0000000..1f2c562
--- /dev/null
@@ -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 (file)
index 0000000..74b191f
--- /dev/null
@@ -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 (file)
index 0000000..1ea3cf6
--- /dev/null
@@ -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 (file)
index 0000000..0f80a58
--- /dev/null
@@ -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 (file)
index 0000000..bf3cf77
--- /dev/null
@@ -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 (file)
index 0000000..526cf47
--- /dev/null
@@ -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 (file)
index 0000000..2d847ee
--- /dev/null
@@ -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 (file)
index cc229a9..0000000
+++ /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 (file)
index 0000000..e6c4e6b
--- /dev/null
@@ -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 (file)
index 0000000..4f91fcd
--- /dev/null
@@ -0,0 +1 @@
+gi-index
index b3d14d7..a4d0cc0 100644 (file)
@@ -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
 
index daa1bda..3ee9ad8 100644 (file)
@@ -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 (file)
index 0000000..7abe3fa
--- /dev/null
@@ -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 (file)
index 0000000..9cc74a4
--- /dev/null
@@ -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 (file)
index 0000000..47076d4
--- /dev/null
@@ -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 (file)
index 0000000..ba153dd
--- /dev/null
@@ -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__ */
index 3266c6e..2197aec 100644 (file)
@@ -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 (file)
index 0000000..45bb51a
--- /dev/null
@@ -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 (file)
index 0000000..e946c1a
--- /dev/null
@@ -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 (file)
index 0000000..51812f4
--- /dev/null
@@ -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 (file)
index 0000000..0e7f29c
--- /dev/null
@@ -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 (file)
index 0000000..9b74ae4
--- /dev/null
@@ -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 (file)
index 0000000..e3814b0
--- /dev/null
@@ -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
index 6a2ba6b..67aeeb4 100644 (file)
@@ -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)
index 607e1e2..ab98016 100644 (file)
@@ -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 (file)
index 0000000..5ae1f82
--- /dev/null
@@ -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 (file)
index 0000000..474bee7
--- /dev/null
@@ -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}
index 5bebe2f..19ad0b5 100644 (file)
@@ -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 (file)
index 0000000..553aea2
--- /dev/null
@@ -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 (file)
index 0000000..3fda1f6
--- /dev/null
@@ -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 (file)
index 0000000..4f855c5
--- /dev/null
@@ -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 (file)
index 0000000..106ec45
--- /dev/null
@@ -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*/