diff --git a/docs/design/Makefile.am b/docs/design/Makefile.am index f2e0b71b6d..357af4ce99 100644 --- a/docs/design/Makefile.am +++ b/docs/design/Makefile.am @@ -4,6 +4,7 @@ SUBDIRS = EXTRA_DIST = \ design-audiosinks.txt \ design-decodebin.txt \ + design-encoding.txt \ design-orc-integration.txt \ draft-keyframe-force.txt \ draft-va.txt \ diff --git a/docs/design/design-encoding.txt b/docs/design/design-encoding.txt new file mode 100644 index 0000000000..dda79addf6 --- /dev/null +++ b/docs/design/design-encoding.txt @@ -0,0 +1,571 @@ +Encoding and Muxing +------------------- + +Summary +------- + A. Problems + B. Goals + 1. EncodeBin + 2. Encoding Profile System + 3. Helper Library for Profiles + I. Use-cases researched + + +A. Problems this proposal attempts to solve +------------------------------------------- + +* Duplication of pipeline code for gstreamer-based applications + wishing to encode and or mux streams, leading to subtle differences + and inconsistencies accross those applications. + +* No unified system for describing encoding targets for applications + in a user-friendly way. + +* No unified system for creating encoding targets for applications, + resulting in duplication of code accross all applications, + differences and inconsistencies that come with that duplication, + and applications hardcoding element names and settings resulting in + poor portability. + + + +B. Goals +-------- + +1. Convenience encoding element + + Create a convenience GstBin for encoding and muxing several streams, + hereafter called 'EncodeBin'. + + This element will only contain one single property, which is a + profile. + +2. Define a encoding profile system + +2. Encoding profile helper library + + Create a helper library to: + * create EncodeBin instances based on profiles, and + * help applications to create/load/save/browse those profiles. + + + + +1. EncodeBin +------------ + +1.1 Proposed API +---------------- + + EncodeBin is a GstBin subclass. + + It implements the GstTagSetter interface, by which it will proxy the + calls to the muxer. + + Only two introspectable property (i.e. usable without extra API): + * A GstEncodingProfile* + * The name of the profile to use + + When a profile is selected, encodebin will: + * Add REQUEST sinkpads for all the GstStreamProfile + * Create the muxer and expose the source pad + + Whenever a request pad is created, encodebin will: + * Create the chain of elements for that pad + * Ghost the sink pad + * Return that ghost pad + + This allows reducing the code to the minimum for applications + wishing to encode a source for a given profile: + + ... + + encbin = gst_element_factory_make("encodebin, NULL); + g_object_set (encbin, "profile", "N900/H264 HQ", NULL); + gst_element_link (encbin, filesink); + + ... + + vsrcpad = gst_element_get_src_pad(source, "src1"); + vsinkpad = gst_element_get_request_pad (encbin, "video_%d"); + gst_pad_link(vsrcpad, vsinkpad); + + ... + + +1.2 Explanation of the Various stages in EncodeBin +-------------------------------------------------- + + This describes the various stages which can happen in order to end + up with a multiplexed stream that can then be stored or streamed. + +1.2.1 Incoming streams + + The streams fed to EncodeBin can be of various types: + + * Video + * Uncompressed (but maybe subsampled) + * Compressed + * Audio + * Uncompressed (audio/x-raw-{int|float}) + * Compressed + * Timed text + * Private streams + + +1.2.2 Steps involved for raw video encoding + +(0) Incoming Stream + +(1) Transform raw video feed (optional) + + Here we modify the various fundamental properties of a raw video + stream to be compatible with the intersection of: + * The encoder GstCaps and + * The specified "Stream Restriction" of the profile/target + + The fundamental properties that can be modified are: + * width/height + This is done with a video scaler. + The DAR (Display Aspect Ratio) MUST be respected. + If needed, black borders can be added to comply with the target DAR. + * framerate + * format/colorspace/depth + All of this is done with a colorspace converter + +(2) Actual encoding (optional for raw streams) + + An encoder (with some optional settings) is used. + +(3) Muxing + + A muxer (with some optional settings) is used. + +(4) Outgoing encoded and muxed stream + + +1.2.3 Steps involved for raw audio encoding + + This is roughly the same as for raw video, expect for (1) + +(1) Transform raw audo feed (optional) + + We modify the various fundamental properties of a raw audio stream to + be compatible with the intersection of: + * The encoder GstCaps and + * The specified "Stream Restriction" of the profile/target + + The fundamental properties that can be modifier are: + * Number of channels + * Type of raw audio (integer or floating point) + * Depth (number of bits required to encode one sample) + + +1.2.4 Steps involved for encoded audio/video streams + + Steps (1) and (2) are replaced by a parser if a parser is available + for the given format. + + +1.2.5 Steps involved for other streams + + Other streams will just be forwarded as-is to the muxer, provided the + muxer accepts the stream type. + + + + +2. Encoding Profile System +-------------------------- + + This work is based on: + * The existing GstPreset system for elements [0] + * The gnome-media GConf audio profile system [1] + * The investigation done into device profiles by Arista and + Transmageddon [2 and 3] + +2.2 Terminology +--------------- + +* Encoding Target Category + A Target Category is a classification of devices/systems/use-cases + for encoding. + + Such a classification is required in order for: + * Applications with a very-specific use-case to limit the number of + profiles they can offer the user. A screencasting application has + no use with the online services targets for example. + * Offering the user some initial classification in the case of a + more generic encoding application (like a video editor or a + transcoder). + + Ex: + Consumer devices + Online service + Intermediate Editing Format + Screencast + Capture + Computer + +* Encoding Profile Target + A Profile Target describes a specific entity for which we wish to + encode. + A Profile Target must belong to at least one Target Category. + It will define at least one Encoding Profile. + + Ex (with category): + Nokia N900 (Consumer device) + Sony PlayStation 3 (Consumer device) + Youtube (Online service) + DNxHD (Intermediate editing format) + HuffYUV (Screencast) + Theora (Computer) + +* Encoding Profile + A specific combination of muxer, encoders, presets and limitations. + + Ex: + Nokia N900/H264 HQ + Ipod/High Quality + DVD/Pal + Youtube/High Quality + HTML5/Low Bandwith + DNxHD + +2.3 Encoding Profile +-------------------- + +An encoding profile requires the following information: + + * Name + This string is not translatable and must be unique. + A recommendation to guarantee uniqueness of the naming could be: + / + * Description + This is a translatable string describing the profile + * Muxing format + This is a string containing the GStreamer media-type of the + container format. + * Muxing preset + This is an optional string describing the preset(s) to use on the + muxer. + * Multipass setting + This is a boolean describing whether the profile requires several + passes. + * List of Stream Profile + +2.3.1 Stream Profiles + +A Stream Profile consists of: + + * Type + The type of stream profile (audio, video, text, private-data) + * Encoding Format + This is a string containing the GStreamer media-type of the encoding + format to be used. If encoding is not to be applied, the raw audio + media type will be used. + * Encoding preset + This is an optional string describing the preset(s) to use on the + encoder. + * Restriction + This is an optional GstCaps containing the restriction of the + stream that can be fed to the encoder. + This will generally containing restrictions in video + width/heigh/framerate or audio depth. + * presence + This is an integer specifying how many streams can be used in the + containing profile. 0 means that any number of streams can be + used. + * pass + This is an integer which is only meaningful if the multipass flag + has been set in the profile. If it has been set it indicates which + pass this Stream Profile corresponds to. + +2.4 Example profile +------------------- + +The representation used here is XML only as an example. No decision is +made as to which formatting to use for storing targets and profiles. + + + Nokia N900 + Consumer Device + + Nokia N900/H264 HQ + Nokia N900/MP3 + Nokia N900/AAC + + + + + Nokia N900/H264 HQ + + High Quality H264/AAC for the Nokia N900 + + video/quicktime,variant=iso + + + audio + audio/mpeg,mpegversion=4 + Quality High/Main + audio/x-raw-int,channels=[1,2] + 1 + + + video + video/x-h264 + Profile Baseline/Quality High + + video/x-raw-yuv,width=[16, 800],\ + height=[16, 480],framerate=[1/1, 30000/1001] + + 1 + + + + + +2.5 API +------- + A proposed C API is contained in the gstprofile.h file in this directory. + + +2.6 Modifications required in the existing GstPreset system +----------------------------------------------------------- + +2.6.1. Temporary preset. + + Currently a preset needs to be saved on disk in order to be + used. + + This makes it impossible to have temporary presets (that exist only + during the lifetime of a process), which might be required in the + new proposed profile system + +2.6.2 Categorisation of presets. + + Currently presets are just aliases of a group of property/value + without any meanings or explanation as to how they exclude each + other. + + Take for example the H264 encoder. It can have presets for: + * passes (1,2 or 3 passes) + * profiles (Baseline, Main, ...) + * quality (Low, medium, High) + + In order to programmatically know which presets exclude each other, + we here propose the categorisation of these presets. + + This can be done in one of two ways + 1. in the name (by making the name be [:]) + This would give for example: "Quality:High", "Profile:Baseline" + 2. by adding a new _meta key + This would give for example: _meta/category:quality + +2.6.3 Aggregation of presets. + + There can be more than one choice of presets to be done for an + element (quality, profile, pass). + + This means that one can not currently describe the full + configuration of an element with a single string but with many. + + The proposal here is to extend the GstPreset API to be able to set + all presets using one string and a well-known separator ('/'). + + This change only requires changes in the core preset handling code. + + This would allow doing the following: + gst_preset_load_preset (h264enc, + "pass:1/profile:baseline/quality:high"); + +2.7 Points to be determined +--------------------------- + + This document hasn't determined yet how to solve the following + problems: + +2.7.1 Storage of profiles + + One proposal for storage would be to use a system wide directory + (like $prefix/share/gstreamer-0.10/profiles) and store XML files for + every individual profiles. + + Users could then add their own profiles in ~/.gstreamer-0.10/profiles + + This poses some limitations as to what to do if some applications + want to have some profiles limited to their own usage. + + +3. Helper library for profiles +------------------------------ + + These helper methods could also be added to existing libraries (like + GstPreset, GstPbUtils, ..). + + The various API proposed are in the accompanying gstprofile.h file. + +3.1 Getting user-readable names for formats + + This is already provided by GstPbUtils. + +3.2 Hierarchy of profiles + + The goal is for applications to be able to present to the user a list + of combo-boxes for choosing their output profile: + + [ Category ] # optional, depends on the application + [ Device/Site/.. ] # optional, depends on the application + [ Profile ] + + Convenience methods are offered to easily get lists of categories, + devices, and profiles. + +3.3 Creating Profiles + + The goal is for applications to be able to easily create profiles. + + The applications needs to be able to have a fast/efficient way to: + * select a container format and see all compatible streams he can use + with it. + * select a codec format and see which container formats he can use + with it. + + The remaining parts concern the restrictions to encoder + input. + +3.4 Ensuring availability of plugins for Profiles + + When an application wishes to use a Profile, it should be able to + query whether it has all the needed plugins to use it. + + This part will use GstPbUtils to query, and if needed install the + missing plugins through the installed distribution plugin installer. + + +I. Use-cases researched +----------------------- + + This is a list of various use-cases where encoding/muxing is being + used. + +* Transcoding + + The goal is to convert with as minimal loss of quality any input + file for a target use. + A specific variant of this is transmuxing (see below). + + Example applications: Arista, Transmageddon + +* Rendering timelines + + The incoming streams are a collection of various segments that need + to be rendered. + Those segments can vary in nature (i.e. the video width/height can + change). + This requires the use of identiy with the single-segment property + activated to transform the incoming collection of segments to a + single continuous segment. + + Example applications: PiTiVi, Jokosher + +* Encoding of live sources + + The major risk to take into account is the encoder not encoding the + incoming stream fast enough. This is outside of the scope of + encodebin, and should be solved by using queues between the sources + and encodebin, as well as implementing QoS in encoders and sources + (the encoders emitting QoS events, and the upstream elements + adapting themselves accordingly). + + Example applications: camerabin, cheese + +* Screencasting applications + + This is similar to encoding of live sources. + The difference being that due to the nature of the source (size and + amount/frequency of updates) one might want to do the encoding in + two parts: + * The actual live capture is encoded with a 'almost-lossless' codec + (such as huffyuv) + * Once the capture is done, the file created in the first step is + then rendered to the desired target format. + + Fixing sources to only emit region-updates and having encoders + capable of encoding those streams would fix the need for the first + step but is outside of the scope of encodebin. + + Example applications: Istanbul, gnome-shell, recordmydesktop + +* Live transcoding + + This is the case of an incoming live stream which will be + broadcasted/transmitted live. + One issue to take into account is to reduce the encoding latency to + a minimum. This should mostly be done by picking low-latency + encoders. + + Example applications: Rygel, Coherence + +* Transmuxing + + Given a certain file, the aim is to remux the contents WITHOUT + decoding into either a different container format or the same + container format. + Remuxing into the same container format is useful when the file was + not created properly (for example, the index is missing). + Whenever available, parsers should be applied on the encoded streams + to validate and/or fix the streams before muxing them. + + Metadata from the original file must be kept in the newly created + file. + + Example applications: Arista, Transmaggedon + +* Loss-less cutting + + Given a certain file, the aim is to extract a certain part of the + file without going through the process of decoding and re-encoding + that file. + This is similar to the transmuxing use-case. + + Example applications: PiTiVi, Transmageddon, Arista, ... + +* Multi-pass encoding + + Some encoders allow doing a multi-pass encoding. + The initial pass(es) are only used to collect encoding estimates and + are not actually muxed and outputted. + The final pass uses previously collected information, and the output + is then muxed and outputted. + +* Archiving and intermediary format + + The requirement is to have lossless + +* CD ripping + + Example applications: Sound-juicer + +* DVD ripping + + Example application: Thoggen + + + +* Research links + + Some of these are still active documents, some other not + +[0] GstPreset API documentation + http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstPreset.html + +[1] gnome-media GConf profiles + http://www.gnome.org/~bmsmith/gconf-docs/C/gnome-media.html + +[2] Research on a Device Profile API + http://gstreamer.freedesktop.org/wiki/DeviceProfile + +[3] Research on defining presets usage + http://gstreamer.freedesktop.org/wiki/PresetDesign + diff --git a/docs/libs/gst-plugins-base-libs-docs.sgml b/docs/libs/gst-plugins-base-libs-docs.sgml index 94a34519a3..b7b61a70a7 100644 --- a/docs/libs/gst-plugins-base-libs-docs.sgml +++ b/docs/libs/gst-plugins-base-libs-docs.sgml @@ -206,6 +206,7 @@ + diff --git a/docs/libs/gst-plugins-base-libs-sections.txt b/docs/libs/gst-plugins-base-libs-sections.txt index aff1cdb04f..da6134bce0 100644 --- a/docs/libs/gst-plugins-base-libs-sections.txt +++ b/docs/libs/gst-plugins-base-libs-sections.txt @@ -1907,6 +1907,91 @@ gst_codec_utils_mpeg4video_get_level gst_codec_utils_mpeg4video_caps_set_level_and_profile +
+encoding-profile +gst/pbutils/encoding-profile.h +GstEncodingProfile +gst_encoding_profile_unref +gst_encoding_profile_ref +gst_encoding_profile_get_name +gst_encoding_profile_get_description +gst_encoding_profile_get_format +gst_encoding_profile_get_preset +gst_encoding_profile_get_presence +gst_encoding_profile_get_restriction +gst_encoding_profile_set_name +gst_encoding_profile_set_description +gst_encoding_profile_set_format +gst_encoding_profile_set_preset +gst_encoding_profile_set_restriction +gst_encoding_profile_set_presence +gst_encoding_profile_is_equal +gst_encoding_profile_get_output_caps +gst_encoding_profile_get_type_nick + +GstEncodingContainerProfile +gst_encoding_container_profile_new +gst_encoding_container_profile_add_profile +gst_encoding_container_profile_contains_profile +gst_encoding_container_profile_get_profiles + +GstEncodingAudioProfile +gst_encoding_audio_profile_new + +GstEncodingVideoProfile +gst_encoding_video_profile_new +gst_encoding_video_profile_get_pass +gst_encoding_video_profile_get_variableframerate +gst_encoding_video_profile_set_pass +gst_encoding_video_profile_set_variableframerate + +GST_ENCODING_CATEGORY_DEVICE +GST_ENCODING_CATEGORY_ONLINE_SERVICE +GST_ENCODING_CATEGORY_STORAGE_EDITING +GST_ENCODING_CATEGORY_CAPTURE +GstEncodingTarget +gst_encoding_target_unref +gst_encoding_target_ref +gst_encoding_target_new +gst_encoding_target_get_name +gst_encoding_target_get_category +gst_encoding_target_get_description +gst_encoding_target_get_profiles +gst_encoding_target_add_profile +gst_encoding_target_save +gst_encoding_target_save_to +gst_encoding_target_load +gst_encoding_target_load_from + +GST_ENCODING_PROFILE +GST_IS_ENCODING_PROFILE +GST_TYPE_ENCODING_PROFILE +gst_encoding_profile_get_type +GST_ENCODING_TARGET +GST_IS_ENCODING_TARGET +GST_TYPE_ENCODING_TARGET +gst_encoding_target_get_type +GstEncodingProfileClass +GST_TYPE_ENCODING_CONTAINER_PROFILE +GST_ENCODING_CONTAINER_PROFILE +gst_encoding_container_profile_get_type +GST_TYPE_ENCODING_VIDEO_PROFILE +GST_ENCODING_VIDEO_PROFILE +GST_IS_ENCODING_VIDEO_PROFILE +GstEncodingVideoProfileClass +gst_encoding_video_profile_get_type +GST_TYPE_ENCODING_AUDIO_PROFILE +GST_ENCODING_AUDIO_PROFILE +GST_IS_ENCODING_AUDIO_PROFILE +GstEncodingAudioProfileClass +gst_encoding_audio_profile_get_type +GST_IS_ENCODING_CONTAINER_PROFILE +GstEncodingContainerProfileClass +GstEncodingTargetClass +
+ + + # video
diff --git a/docs/libs/gst-plugins-base-libs.types b/docs/libs/gst-plugins-base-libs.types index db3818a1d3..f6c06af2cd 100644 --- a/docs/libs/gst-plugins-base-libs.types +++ b/docs/libs/gst-plugins-base-libs.types @@ -59,3 +59,14 @@ gst_video_sink_get_type #include gst_discoverer_get_type + +#include +#include +gst_encoding_profile_get_type +gst_encoding_video_profile_get_type +gst_encoding_video_profile_get_type +gst_encoding_audio_profile_get_type +gst_encoding_container_profile_get_type +gst_encoding_target_get_type + + diff --git a/gst-libs/gst/pbutils/Makefile.am b/gst-libs/gst/pbutils/Makefile.am index 9a261f51b7..73251e1aef 100644 --- a/gst-libs/gst/pbutils/Makefile.am +++ b/gst-libs/gst/pbutils/Makefile.am @@ -4,6 +4,8 @@ headers_pbutils = \ pbutils.h \ codec-utils.h \ descriptions.h \ + encoding-profile.h \ + encoding-target.h \ install-plugins.h \ missing-plugins.h \ gstdiscoverer.h @@ -22,6 +24,8 @@ libgstpbutils_@GST_MAJORMINOR@_la_SOURCES = \ pbutils.c \ codec-utils.c \ descriptions.c \ + encoding-profile.c \ + encoding-target.c \ install-plugins.c \ missing-plugins.c \ gstdiscoverer.c \ diff --git a/gst-libs/gst/pbutils/encoding-profile.c b/gst-libs/gst/pbutils/encoding-profile.c new file mode 100644 index 0000000000..8c364c8878 --- /dev/null +++ b/gst-libs/gst/pbutils/encoding-profile.c @@ -0,0 +1,834 @@ +/* GStreamer encoding profiles library + * Copyright (C) 2009-2010 Edward Hervey + * (C) 2009-2010 Nokia Corporation + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:encoding-profile + * @short_description: Encoding profile library + * + * + * + * Functions to create and handle encoding profiles. + * + * + * Encoding profiles describe the media types and settings one wishes to use for + * an encoding process. The top-level profiles are commonly + * #GstEncodingContainerProfile(s) (which contains user-readable name and + * description along with which container format to use) which references one or + * more #GstEncodingProfile(s) which indicate which encoding format should be + * used on each individual streams. + * + * + * #GstEncodingProfile(s) can be provided to the 'encodebin' element, which will take + * care of selecting and setting up the required elements to produce an output stream + * conforming to the specifications of the profile. + * + * + * Unlike other systems, the encoding profiles do not specify which #GstElement to use + * for the various encoding and muxing steps, but instead relies on specifying the format + * one wishes to use. + * + * + * Encoding profiles can be created at runtime by the application or loaded from (and saved + * to) file using the #GstEncodingTarget API. + * + * + * + * Example: Creating a profile + * + * |[ + * #include + * ... + * GstEncodingProfile * + * create_ogg_theora_profile(void) + *{ + * GstEncodingContainerProfile *prof; + * GstCaps *caps; + * + * caps = gst_caps_from_string("application/ogg"); + * prof = gst_encoding_container_profile_new("Ogg audio/video", + * "Standard OGG/THEORA/VORBIS", + * caps, NULL); + * gst_caps_unref (caps); + * + * caps = gst_caps_from_string("video/x-theora"); + * sprof = gst_encoding_container_profile_add_profile( + * (GstEncodingProfile*) gst_encoding_video_profile_new(caps, NULL, NULL, 0)); + * gst_caps_unref (caps); + * + * caps = gst_caps_from_string("audio/x-vorbis"); + * sprof = gst_encoding_container_profile_add_profile( + * (GstEncodingProfile*) gst_encoding_audio_profile_new(caps, NULL, NULL, 0)); + * gst_caps_unref (caps); + * + * return (GstEncodingProfile*) prof; + *} + * + * + * ]| + * + * + * + * Example: Loading a profile from disk + * + * |[ + * #include + * ... + *GstEncodingProfile * + *get_ogg_theora_profile(const gchar *path, const gchar *profilename) + *{ + * GstEncodingProfile *prof = NULL; + * GstEncodingTarget *target = NULL; + * GList *tmp; + * + * target = gst_encoding_target_load_from (path); + * if (target == NULL) + * return NULL; + * + * for (tmp = target->profiles; tmp; tmp = tmp->next) { + * GstEncodingProfile *ptmp = (GstEncodingProfile*) tmp->data; + * + * if (!strcmp(gst_encoding_profile_get_name(ptmp), profilename)) { + * prof = ptmp; + * break; + * } + * } + * + * return prof; + *} + * ]| + * + * + * + * Since: 0.10.32 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "encoding-profile.h" + +/* GstEncodingProfile API */ + +struct _GstEncodingProfile +{ + GstMiniObject parent; + + /*< public > */ + gchar *name; + gchar *description; + GstCaps *format; + gchar *preset; + guint presence; + GstCaps *restriction; +}; + +G_DEFINE_TYPE (GstEncodingProfile, gst_encoding_profile, GST_TYPE_MINI_OBJECT); + +static void +gst_encoding_profile_init (GstEncodingProfile * prof) +{ + /* Nothing to initialize */ +} + +static void +gst_encoding_profile_finalize (GstEncodingProfile * prof) +{ + if (prof->name) + g_free (prof->name); + if (prof->format) + gst_caps_unref (prof->format); + if (prof->preset) + g_free (prof->preset); + if (prof->description) + g_free (prof->description); + if (prof->restriction) + gst_caps_unref (prof->restriction); +} + +static void +gst_encoding_profile_class_init (GstMiniObjectClass * klass) +{ + klass->finalize = + (GstMiniObjectFinalizeFunction) gst_encoding_profile_finalize; +} + +/** + * gst_encoding_profile_get_name: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: the name of the profile, can be %NULL. + */ +const gchar * +gst_encoding_profile_get_name (GstEncodingProfile * profile) +{ + return profile->name; +} + +/** + * gst_encoding_profile_get_description: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: the description of the profile, can be %NULL. + */ +const gchar * +gst_encoding_profile_get_description (GstEncodingProfile * profile) +{ + return profile->description; +} + +/** + * gst_encoding_profile_get_format: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: the media format used in the profile. + */ +const GstCaps * +gst_encoding_profile_get_format (GstEncodingProfile * profile) +{ + return profile->format; +} + +/** + * gst_encoding_profile_get_preset: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: the preset to be used in the profile. + */ +const gchar * +gst_encoding_profile_get_preset (GstEncodingProfile * profile) +{ + return profile->preset; +} + +/** + * gst_encoding_profile_get_presence: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: The number of time the profile is used in its parent + * container profile. If 0, it is not a mandatory stream + */ +const guint +gst_encoding_profile_get_presence (GstEncodingProfile * profile) +{ + return profile->presence; +} + +/** + * gst_encoding_profile_get_restriction: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: The restriction #GstCaps to apply before the encoder + * that will be used in the profile. Does not apply to #GstEncodingContainerProfile. + * Can be %NULL. + */ +const GstCaps * +gst_encoding_profile_get_restriction (GstEncodingProfile * profile) +{ + return profile->restriction; +} + +/** + * gst_encoding_profile_set_name: + * @profile: a #GstEncodingProfile + * @name: the name to set on the profile + * + * Since: 0.10.32 + * + * Set @name as the given name for the @profile. A copy of @name will be made + * internally. + */ +void +gst_encoding_profile_set_name (GstEncodingProfile * profile, const gchar * name) +{ + if (profile->name) + g_free (profile->name); + profile->name = g_strdup (name); +} + +/** + * gst_encoding_profile_set_description: + * @profile: a #GstEncodingProfile + * @description: the description to set on the profile + * + * Since: 0.10.32 + * + * Set @description as the given description for the @profile. A copy of @description will be made + * internally. + */ +void +gst_encoding_profile_set_description (GstEncodingProfile * profile, + const gchar * description) +{ + if (profile->description) + g_free (profile->description); + profile->description = g_strdup (description); +} + +/** + * gst_encoding_profile_set_format: + * @profile: a #GstEncodingProfile + * @format: the media format to use in the profile. + * + * Since: 0.10.32 + * + * Sets the media format used in the profile. + */ +void +gst_encoding_profile_set_format (GstEncodingProfile * profile, GstCaps * format) +{ + if (profile->format) + gst_caps_unref (profile->format); + profile->format = gst_caps_ref (format); +} + +/** + * gst_encoding_profile_set_preset: + * @profile: a #GstEncodingProfile + * @preset: the element preset to use + * + * Since: 0.10.32 + * + * Sets the preset to use for the profile. + */ +void +gst_encoding_profile_set_preset (GstEncodingProfile * profile, + const gchar * preset) +{ + if (profile->preset) + g_free (profile->preset); + profile->preset = g_strdup (preset); +} + +/** + * gst_encoding_profile_set_presence: + * @profile: a #GstEncodingProfile + * @presence: the number of time the profile can be used + * + * Since: 0.10.32 + * + * Set the number of time the profile is used in its parent + * container profile. If 0, it is not a mandatory stream + */ +void +gst_encoding_profile_set_presence (GstEncodingProfile * profile, guint presence) +{ + profile->presence = presence; +} + +/** + * gst_encoding_profile_set_restriction: + * @profile: a #GstEncodingProfile + * @restriction: the restriction to apply + * + * Since: 0.10.32 + * + * Set the restriction #GstCaps to apply before the encoder + * that will be used in the profile. Does not apply to #GstEncodingContainerProfile. + */ +void +gst_encoding_profile_set_restriction (GstEncodingProfile * profile, + GstCaps * restriction) +{ + if (profile->restriction) + gst_caps_unref (profile->restriction); + profile->restriction = restriction; +} + +/* Container profiles */ + +struct _GstEncodingContainerProfile +{ + GstEncodingProfile parent; + + GList *encodingprofiles; +}; + +G_DEFINE_TYPE (GstEncodingContainerProfile, gst_encoding_container_profile, + GST_TYPE_ENCODING_PROFILE); + +static void +gst_encoding_container_profile_init (GstEncodingContainerProfile * prof) +{ + /* Nothing to initialize */ +} + +static void +gst_encoding_container_profile_finalize (GstEncodingContainerProfile * prof) +{ + g_list_foreach (prof->encodingprofiles, (GFunc) gst_mini_object_unref, NULL); + g_list_free (prof->encodingprofiles); + + GST_MINI_OBJECT_CLASS (gst_encoding_container_profile_parent_class)->finalize + ((GstMiniObject *) prof); +} + +static void +gst_encoding_container_profile_class_init (GstMiniObjectClass * klass) +{ + klass->finalize = + (GstMiniObjectFinalizeFunction) gst_encoding_container_profile_finalize; +} + +const GList * +gst_encoding_container_profile_get_profiles (GstEncodingContainerProfile * + profile) +{ + return profile->encodingprofiles; +} + +/* Video profiles */ + +struct _GstEncodingVideoProfile +{ + GstEncodingProfile parent; + + guint pass; + gboolean variableframerate; +}; + +G_DEFINE_TYPE (GstEncodingVideoProfile, gst_encoding_video_profile, + GST_TYPE_ENCODING_PROFILE); + +static void +gst_encoding_video_profile_init (GstEncodingVideoProfile * prof) +{ + /* Nothing to initialize */ +} + +static void +gst_encoding_video_profile_class_init (GstMiniObjectClass * klass) +{ +} + +/** + * gst_encoding_video_profile_get_pass: + * @prof: a #GstEncodingVideoProfile + * + * Since: 0.10.32 + * + * Returns: The pass number if this is part of a multi-pass profile. Starts at + * 1 for multi-pass. 0 if this is not a multi-pass profile + **/ +guint +gst_encoding_video_profile_get_pass (GstEncodingVideoProfile * prof) +{ + return prof->pass; +} + +/** + * gst_encoding_video_profile_get_variableframerate: + * @prof: a #GstEncodingVideoProfile + * + * Since: 0.10.32 + * + * Returns: Whether non-constant video framerate is allowed for encoding. + */ +gboolean +gst_encoding_video_profile_get_variableframerate (GstEncodingVideoProfile * + prof) +{ + return prof->variableframerate; +} + +/** + * gst_encoding_video_profile_set_pass: + * @prof: a #GstEncodingVideoProfile + * @pass: the pass number for this profile + * + * Since: 0.10.32 + * + * Sets the pass number of this video profile. The first pass profile should have + * this value set to 1. If this video profile isn't part of a multi-pass profile, + * you may set it to 0 (the default value). + */ +void +gst_encoding_video_profile_set_pass (GstEncodingVideoProfile * prof, guint pass) +{ + prof->pass = pass; +} + +/** + * gst_encoding_video_profile_set_variableframerate: + * @prof: a #GstEncodingVideoProfile + * @variableframerate: a boolean + * + * Since: 0.10.32 + * + * If set to %TRUE, then the incoming streamm will be allowed to have non-constant + * framerate. If set to %FALSE (default value), then the incoming stream will + * be normalized by dropping/duplicating frames in order to produce a + * constance framerate. + */ +void +gst_encoding_video_profile_set_variableframerate (GstEncodingVideoProfile * + prof, gboolean variableframerate) +{ + prof->variableframerate = variableframerate; +} + +/* Audio profiles */ + +struct _GstEncodingAudioProfile +{ + GstEncodingProfile parent; +}; + +G_DEFINE_TYPE (GstEncodingAudioProfile, gst_encoding_audio_profile, + GST_TYPE_ENCODING_PROFILE); + +static void +gst_encoding_audio_profile_init (GstEncodingAudioProfile * prof) +{ + /* Nothing to initialize */ +} + +static void +gst_encoding_audio_profile_class_init (GstMiniObjectClass * klass) +{ +} + +static inline gboolean +_gst_caps_is_equal_safe (GstCaps * a, GstCaps * b) +{ + if (a == b) + return TRUE; + if ((a == NULL) || (b == NULL)) + return FALSE; + return gst_caps_is_equal (a, b); +} + +static gint +_compare_container_encoding_profiles (GstEncodingContainerProfile * ca, + GstEncodingContainerProfile * cb) +{ + GList *tmp; + + if (g_list_length (ca->encodingprofiles) != + g_list_length (cb->encodingprofiles)) + return -1; + + for (tmp = ca->encodingprofiles; tmp; tmp = tmp->next) { + GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data; + if (!gst_encoding_container_profile_contains_profile (ca, prof)) + return -1; + } + + return 0; +} + +static gint +_compare_encoding_profiles (const GstEncodingProfile * a, + const GstEncodingProfile * b) +{ + if ((G_TYPE_FROM_INSTANCE (a) != G_TYPE_FROM_INSTANCE (b)) || + !_gst_caps_is_equal_safe (a->format, b->format) || + (g_strcmp0 (a->preset, b->preset) != 0) || + (g_strcmp0 (a->name, b->name) != 0) || + (g_strcmp0 (a->description, b->description) != 0)) + return -1; + + if (GST_IS_ENCODING_CONTAINER_PROFILE (a)) + return + _compare_container_encoding_profiles (GST_ENCODING_CONTAINER_PROFILE + (a), GST_ENCODING_CONTAINER_PROFILE (b)); + + if (GST_IS_ENCODING_VIDEO_PROFILE (a)) { + GstEncodingVideoProfile *va = (GstEncodingVideoProfile *) a; + GstEncodingVideoProfile *vb = (GstEncodingVideoProfile *) b; + + if ((va->pass != vb->pass) + || (va->variableframerate != vb->variableframerate)) + return -1; + } + + return 0; +} + +/** + * gst_encoding_container_profile_contains_profile: + * @container: a #GstEncodingContainerProfile + * @profile: a #GstEncodingProfile + * + * Checks if @container contains a #GstEncodingProfile identical to + * @profile. + * + * Since: 0.10.32 + * + * Returns: %TRUE if @container contains a #GstEncodingProfile identical + * to @profile, else %FALSE. + */ +gboolean +gst_encoding_container_profile_contains_profile (GstEncodingContainerProfile * + container, GstEncodingProfile * profile) +{ + g_return_val_if_fail (GST_IS_ENCODING_CONTAINER_PROFILE (container), FALSE); + g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); + + return (g_list_find_custom (container->encodingprofiles, profile, + (GCompareFunc) _compare_encoding_profiles) != NULL); +} + +/** + * gst_encoding_container_profile_add_profile: + * @container: the #GstEncodingContainerProfile to use + * @profile: the #GstEncodingProfile to add. + * + * Add a #GstEncodingProfile to the list of profiles handled by @container. + * + * No copy of @profile will be made, if you wish to use it elsewhere after this + * method you should increment its reference count. + * + * Since: 0.10.32 + * + * Returns: %TRUE if the @stream was properly added, else %FALSE. + */ +gboolean +gst_encoding_container_profile_add_profile (GstEncodingContainerProfile * + container, GstEncodingProfile * profile) +{ + g_return_val_if_fail (GST_IS_ENCODING_CONTAINER_PROFILE (container), FALSE); + g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); + + if (g_list_find_custom (container->encodingprofiles, profile, + (GCompareFunc) _compare_encoding_profiles)) { + GST_ERROR + ("Encoding profile already contains an identical GstEncodingProfile"); + return FALSE; + } + + container->encodingprofiles = + g_list_append (container->encodingprofiles, profile); + + return TRUE; +} + +static GstEncodingProfile * +common_creation (GType objtype, GstCaps * format, const gchar * preset, + const gchar * name, const gchar * description, GstCaps * restriction, + guint presence) +{ + GstEncodingProfile *prof; + + prof = (GstEncodingProfile *) gst_mini_object_new (objtype); + + if (name) + prof->name = g_strdup (name); + if (description) + prof->description = g_strdup (description); + if (preset) + prof->preset = g_strdup (preset); + if (format) + prof->format = gst_caps_ref (format); + if (restriction) + prof->restriction = gst_caps_ref (restriction); + prof->presence = presence; + + return prof; +} + +/** + * gst_encoding_container_profile_new: + * @name: The name of the container profile, can be %NULL + * @description: The description of the container profile, can be %NULL + * @format: The format to use for this profile + * @preset: The preset to use for this profile + * + * Creates a new #GstEncodingContainerProfile. + * + * Since: 0.10.32 + * + * Returns: The newly created #GstEncodingContainerProfile. + */ +GstEncodingContainerProfile * +gst_encoding_container_profile_new (const gchar * name, + const gchar * description, GstCaps * format, const gchar * preset) +{ + return (GstEncodingContainerProfile *) + common_creation (GST_TYPE_ENCODING_CONTAINER_PROFILE, format, preset, + name, description, NULL, 0); +} + +/** + * gst_encoding_video_profile_new: + * @format: the #GstCaps + * @preset: the preset(s) to use on the encoder, can be #NULL + * @restriction: the #GstCaps used to restrict the input to the encoder, can be + * NULL. + * @presence: the number of time this stream must be used. 0 means any number of + * times (including never) + * + * Creates a new #GstEncodingVideoProfile + * + * All provided allocatable arguments will be internally copied, so can be + * safely freed/unreferenced after calling this method. + * + * If you wish to control the pass number (in case of multi-pass scenarios), + * please refer to the gst_encoding_video_profile_set_pass() documentation. + * + * If you wish to use/force a constant framerate please refer to the + * gst_encoding_video_profile_set_variableframerate() documentation. + * + * Since: 0.10.32 + * + * Returns: the newly created #GstEncodingVideoProfile. + */ +GstEncodingVideoProfile * +gst_encoding_video_profile_new (GstCaps * format, const gchar * preset, + GstCaps * restriction, guint presence) +{ + return (GstEncodingVideoProfile *) + common_creation (GST_TYPE_ENCODING_VIDEO_PROFILE, format, preset, NULL, + NULL, restriction, presence); +} + +/** + * gst_encoding_audio_profile_new: + * @format: the #GstCaps + * @preset: the preset(s) to use on the encoder, can be #NULL + * @restriction: the #GstCaps used to restrict the input to the encoder, can be + * NULL. + * @presence: the number of time this stream must be used. 0 means any number of + * times (including never) + * + * Creates a new #GstEncodingAudioProfile + * + * All provided allocatable arguments will be internally copied, so can be + * safely freed/unreferenced after calling this method. + * + * Since: 0.10.32 + * + * Returns: the newly created #GstEncodingAudioProfile. + */ +GstEncodingAudioProfile * +gst_encoding_audio_profile_new (GstCaps * format, const gchar * preset, + GstCaps * restriction, guint presence) +{ + return (GstEncodingAudioProfile *) + common_creation (GST_TYPE_ENCODING_AUDIO_PROFILE, format, preset, NULL, + NULL, restriction, presence); +} + + +/** + * gst_encoding_profile_is_equal: + * @a: a #GstEncodingProfile + * @b: a #GstEncodingProfile + * + * Checks whether the two #GstEncodingProfile are equal + * + * Since: 0.10.32 + * + * Returns: %TRUE if @a and @b are equal, else %FALSE. + */ +gboolean +gst_encoding_profile_is_equal (GstEncodingProfile * a, GstEncodingProfile * b) +{ + return (_compare_encoding_profiles (a, b) == 0); +} + + +/** + * gst_encoding_profile_get_output_caps: + * @profile: a #GstEncodingProfile + * + * Computes the full output caps that this @profile will be able to consume. + * + * Since: 0.10.32 + * + * Returns: The full caps the given @profile can consume. Call gst_caps_unref() + * when you are done with the caps. + */ +GstCaps * +gst_encoding_profile_get_output_caps (GstEncodingProfile * profile) +{ + GstCaps *out, *tmp; + GList *ltmp; + GstStructure *st, *outst; + GQuark out_name; + guint i, len; + const GstCaps *fcaps; + + if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) { + GstCaps *res = gst_caps_new_empty (); + + for (ltmp = GST_ENCODING_CONTAINER_PROFILE (profile)->encodingprofiles; + ltmp; ltmp = ltmp->next) { + GstEncodingProfile *sprof = (GstEncodingProfile *) ltmp->data; + gst_caps_merge (res, gst_encoding_profile_get_output_caps (sprof)); + } + return res; + } + + fcaps = profile->format; + + /* fast-path */ + if ((profile->restriction == NULL) || gst_caps_is_any (profile->restriction)) + return gst_caps_copy (fcaps); + + /* Combine the format with the restriction caps */ + outst = gst_caps_get_structure (fcaps, 0); + out_name = gst_structure_get_name_id (outst); + tmp = gst_caps_new_empty (); + len = gst_caps_get_size (profile->restriction); + + for (i = 0; i < len; i++) { + st = gst_structure_copy (gst_caps_get_structure (profile->restriction, i)); + st->name = out_name; + gst_caps_append_structure (tmp, st); + } + + out = gst_caps_intersect (tmp, fcaps); + gst_caps_unref (tmp); + + return out; +} + +/** + * gst_encoding_profile_get_type_nick: + * @profile: a #GstEncodingProfile + * + * Since: 0.10.32 + * + * Returns: the human-readable name of the type of @profile. + */ +const gchar * +gst_encoding_profile_get_type_nick (GstEncodingProfile * profile) +{ + if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) + return "container"; + if (GST_IS_ENCODING_VIDEO_PROFILE (profile)) + return "video"; + if (GST_IS_ENCODING_AUDIO_PROFILE (profile)) + return "audio"; + return NULL; +} diff --git a/gst-libs/gst/pbutils/encoding-profile.h b/gst-libs/gst/pbutils/encoding-profile.h new file mode 100644 index 0000000000..3c6f3c3d37 --- /dev/null +++ b/gst-libs/gst/pbutils/encoding-profile.h @@ -0,0 +1,184 @@ +/* GStreamer encoding profiles library + * Copyright (C) 2009-2010 Edward Hervey + * (C) 2009-2010 Nokia Corporation + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_PROFILE_H__ +#define __GST_PROFILE_H__ + +#include + +G_BEGIN_DECLS + +#include + +/** + * GstEncodingProfile: + * + * The opaque base class object for all encoding profiles. This contains generic + * information like name, description, format and preset. + * + * Since: 0.10.32 + */ + +#define GST_TYPE_ENCODING_PROFILE \ + (gst_encoding_profile_get_type ()) +#define GST_ENCODING_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODING_PROFILE, GstEncodingProfile)) +#define GST_IS_ENCODING_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODING_PROFILE)) +typedef struct _GstEncodingProfile GstEncodingProfile; +typedef GstMiniObjectClass GstEncodingProfileClass; +GType gst_encoding_profile_get_type (void); + + + +/** + * GstEncodingContainerProfile: + * + * Encoding profiles for containers. Keeps track of a list of #GstEncodingProfile + * + * Since: 0.10.32 + */ +#define GST_TYPE_ENCODING_CONTAINER_PROFILE \ + (gst_encoding_container_profile_get_type ()) +#define GST_ENCODING_CONTAINER_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODING_CONTAINER_PROFILE, GstEncodingContainerProfile)) +#define GST_IS_ENCODING_CONTAINER_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODING_CONTAINER_PROFILE)) +typedef struct _GstEncodingContainerProfile GstEncodingContainerProfile; +typedef GstEncodingProfileClass GstEncodingContainerProfileClass; +GType gst_encoding_container_profile_get_type (void); + + + +/** + * GstEncodingVideoProfile: + * + * Variant of #GstEncodingProfile for video streams, allows specifying the @pass. + * + * Since: 0.10.32 + */ +#define GST_TYPE_ENCODING_VIDEO_PROFILE \ + (gst_encoding_video_profile_get_type ()) +#define GST_ENCODING_VIDEO_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODING_VIDEO_PROFILE, GstEncodingVideoProfile)) +#define GST_IS_ENCODING_VIDEO_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODING_VIDEO_PROFILE)) +typedef struct _GstEncodingVideoProfile GstEncodingVideoProfile; +typedef GstEncodingProfileClass GstEncodingVideoProfileClass; +GType gst_encoding_video_profile_get_type (void); + + + +/** + * GstEncodingAudioProfile: + * + * Variant of #GstEncodingProfile for audio streams. + * + * Since: 0.10.32 + */ +#define GST_TYPE_ENCODING_AUDIO_PROFILE \ + (gst_encoding_audio_profile_get_type ()) +#define GST_ENCODING_AUDIO_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODING_AUDIO_PROFILE, GstEncodingAudioProfile)) +#define GST_IS_ENCODING_AUDIO_PROFILE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODING_AUDIO_PROFILE)) +typedef struct _GstEncodingAudioProfile GstEncodingAudioProfile; +typedef GstEncodingProfileClass GstEncodingAudioProfileClass; +GType gst_encoding_audio_profile_get_type (void); + + + +/* GstEncodingProfile API */ + +/** + * gst_encoding_profile_unref: + * @profile: a #GstEncodingProfile + * + * Decreases the reference count of the @profile, possibly freeing the @profile. + * + * Since: 0.10.32 + */ +#define gst_encoding_profile_unref(profile) (gst_mini_object_unref ((GstMiniObject*) profile)) + +/** + * gst_encoding_profile_ref: + * @profile: a #GstEncodingProfile + * + * Increases the reference count of the @profile. + * + * Since: 0.10.32 + */ +#define gst_encoding_profile_ref(profile) (gst_mini_object_ref ((GstMiniObject*) profile)) + +const gchar * gst_encoding_profile_get_name(GstEncodingProfile *profile); +const gchar * gst_encoding_profile_get_description(GstEncodingProfile *profile); +const GstCaps * gst_encoding_profile_get_format(GstEncodingProfile *profile); +const gchar * gst_encoding_profile_get_preset(GstEncodingProfile *profile); +const guint gst_encoding_profile_get_presence(GstEncodingProfile *profile); +const GstCaps * gst_encoding_profile_get_restriction(GstEncodingProfile *profile); + +void gst_encoding_profile_set_name(GstEncodingProfile *profile, const gchar *name); +void gst_encoding_profile_set_description(GstEncodingProfile *profile, const gchar *description); +void gst_encoding_profile_set_format(GstEncodingProfile *profile, GstCaps *format); +void gst_encoding_profile_set_preset(GstEncodingProfile *profile, const gchar *preset); +void gst_encoding_profile_set_restriction(GstEncodingProfile *profile, GstCaps *restriction); +void gst_encoding_profile_set_presence(GstEncodingProfile *profile, guint presence); + +gboolean gst_encoding_profile_is_equal (GstEncodingProfile *a, + GstEncodingProfile *b); +GstCaps * gst_encoding_profile_get_output_caps (GstEncodingProfile *profile); + +const gchar *gst_encoding_profile_get_type_nick (GstEncodingProfile *profile); + +/* GstEncodingContainerProfile API */ +gboolean gst_encoding_container_profile_add_profile (GstEncodingContainerProfile *container, + GstEncodingProfile *profile); +gboolean gst_encoding_container_profile_contains_profile (GstEncodingContainerProfile * container, + GstEncodingProfile *profile); +const GList *gst_encoding_container_profile_get_profiles (GstEncodingContainerProfile *profile); + + +GstEncodingContainerProfile * gst_encoding_container_profile_new (const gchar *name, + const gchar *description, + GstCaps *format, + const gchar *preset); + + +/* Invidual stream encodingprofile API */ +GstEncodingVideoProfile * gst_encoding_video_profile_new (GstCaps *format, + const gchar *preset, + GstCaps *restriction, + guint presence); +GstEncodingAudioProfile * gst_encoding_audio_profile_new (GstCaps *format, + const gchar *preset, + GstCaps *restriction, + guint presence); + +guint gst_encoding_video_profile_get_pass (GstEncodingVideoProfile *prof); +gboolean gst_encoding_video_profile_get_variableframerate (GstEncodingVideoProfile *prof); + +void gst_encoding_video_profile_set_pass (GstEncodingVideoProfile *prof, + guint pass); +void gst_encoding_video_profile_set_variableframerate (GstEncodingVideoProfile *prof, + gboolean variableframerate); + +G_END_DECLS + +#endif /* __GST_PROFILE_H__ */ diff --git a/gst-libs/gst/pbutils/encoding-target.c b/gst-libs/gst/pbutils/encoding-target.c new file mode 100644 index 0000000000..19065e8fe7 --- /dev/null +++ b/gst-libs/gst/pbutils/encoding-target.c @@ -0,0 +1,724 @@ +/* GStreamer encoding profile registry + * Copyright (C) 2010 Edward Hervey + * (C) 2010 Nokia Corporation + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "encoding-target.h" + +/* + * File format + * + * GKeyFile style. + * + * [_gstencodingtarget_] + * name : + * category : + * description : #translatable + * + * [profile-] + * name : + * description : #optional + * format : + * preset : + * + * [streamprofile-] + * parent : [,..] + * type : # "audio", "video", "text" + * format : + * preset : + * restriction : + * presence : + * pass : + * variableframerate : + * */ + +#define GST_ENCODING_TARGET_HEADER "_gstencodingtarget_" + +struct _GstEncodingTarget +{ + GstMiniObject parent; + + gchar *name; + gchar *category; + gchar *description; + GList *profiles; + + /*< private > */ + gchar *keyfile; +}; + +G_DEFINE_TYPE (GstEncodingTarget, gst_encoding_target, GST_TYPE_MINI_OBJECT); + +static void +gst_encoding_target_init (GstEncodingTarget * target) +{ + /* Nothing to initialize */ +} + +static void +gst_encoding_target_finalize (GstEncodingTarget * target) +{ + GST_DEBUG ("Finalizing"); + + if (target->name) + g_free (target->name); + if (target->category) + g_free (target->category); + if (target->description) + g_free (target->description); + + g_list_foreach (target->profiles, (GFunc) gst_mini_object_unref, NULL); + g_list_free (target->profiles); +} + +static void +gst_encoding_target_class_init (GstMiniObjectClass * klass) +{ + klass->finalize = + (GstMiniObjectFinalizeFunction) gst_encoding_target_finalize; +} + +/** + * gst_encoding_target_get_name: + * @target: a #GstEncodingTarget + * + * Since: 0.10.32 + * + * Returns: The name of the @target. + */ +const gchar * +gst_encoding_target_get_name (GstEncodingTarget * target) +{ + return target->name; +} + +/** + * gst_encoding_target_get_category: + * @target: a #GstEncodingTarget + * + * Since: 0.10.32 + * + * Returns: The category of the @target. + */ +const gchar * +gst_encoding_target_get_category (GstEncodingTarget * target) +{ + return target->category; +} + +/** + * gst_encoding_target_get_description: + * @target: a #GstEncodingTarget + * + * Since: 0.10.32 + * + * Returns: The description of the @target. + */ +const gchar * +gst_encoding_target_get_description (GstEncodingTarget * target) +{ + return target->description; +} + +/** + * gst_encoding_target_get_profiles: + * @target: a #GstEncodingTarget + * + * Since: 0.10.32 + * + * Returns: A list of #GstEncodingProfile(s) this @target handles. + */ +const GList * +gst_encoding_target_get_profiles (GstEncodingTarget * target) +{ + return target->profiles; +} + + +/** + * gst_encoding_target_new: + * @name: The name of the target. + * @category: The name of the category to which this @target belongs. + * @description: A description of #GstEncodingTarget in the current locale. + * @profiles: A #GList of #GstEncodingProfile. + * + * Creates a new #GstEncodingTarget. + * + * Since: 0.10.32 + * + * Returns: The newly created #GstEncodingTarget or %NULL if there was an + * error. + */ + +GstEncodingTarget * +gst_encoding_target_new (const gchar * name, const gchar * category, + const gchar * description, const GList * profiles) +{ + GstEncodingTarget *res; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (category != NULL, NULL); + g_return_val_if_fail (description != NULL, NULL); + + res = (GstEncodingTarget *) gst_mini_object_new (GST_TYPE_ENCODING_TARGET); + res->name = g_strdup (name); + res->category = g_strdup (category); + res->description = g_strdup (description); + + while (profiles) { + GstEncodingProfile *prof = (GstEncodingProfile *) profiles->data; + + res->profiles = + g_list_append (res->profiles, gst_encoding_profile_ref (prof)); + profiles = profiles->next; + } + + return res; +} + +/** + * gst_encoding_target_add_profile: + * @target: the #GstEncodingTarget to add a profile to + * @profile: the #GstEncodingProfile to add + * + * Adds the given @profile to the @target. + * + * The @target will steal a reference to the @profile. If you wish to use + * the profile after calling this method, you should increase its reference + * count. + * + * Since: 0.10.32 + * + * Returns: %TRUE if the profile was added, else %FALSE. + **/ + +gboolean +gst_encoding_target_add_profile (GstEncodingTarget * target, + GstEncodingProfile * profile) +{ + GList *tmp; + + g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE); + g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); + + /* Make sure profile isn't already controlled by this target */ + for (tmp = target->profiles; tmp; tmp = tmp->next) { + GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data; + + if (!g_strcmp0 (gst_encoding_profile_get_name (profile), + gst_encoding_profile_get_name (prof))) { + GST_WARNING ("Profile already present in target"); + return FALSE; + } + } + + target->profiles = g_list_append (target->profiles, profile); + + return TRUE; +} + +static gboolean +serialize_stream_profiles (GKeyFile * out, GstEncodingProfile * sprof, + const gchar * profilename, guint id) +{ + gchar *sprofgroupname; + gchar *tmpc; + const GstCaps *format, *restriction; + const gchar *preset, *name, *description; + + sprofgroupname = g_strdup_printf ("streamprofile-%s-%d", profilename, id); + + /* Write the parent profile */ + g_key_file_set_value (out, sprofgroupname, "parent", profilename); + + g_key_file_set_value (out, sprofgroupname, "type", + gst_encoding_profile_get_type_nick (sprof)); + + format = gst_encoding_profile_get_format (sprof); + if (format) { + tmpc = gst_caps_to_string (format); + g_key_file_set_value (out, sprofgroupname, "format", tmpc); + g_free (tmpc); + } + + name = gst_encoding_profile_get_name (sprof); + if (name) + g_key_file_set_string (out, sprofgroupname, "name", name); + + description = gst_encoding_profile_get_description (sprof); + if (description) + g_key_file_set_string (out, sprofgroupname, "description", description); + + preset = gst_encoding_profile_get_preset (sprof); + if (preset) + g_key_file_set_string (out, sprofgroupname, "preset", preset); + + restriction = gst_encoding_profile_get_restriction (sprof); + if (restriction) { + tmpc = gst_caps_to_string (restriction); + g_key_file_set_value (out, sprofgroupname, "restriction", tmpc); + g_free (tmpc); + } + g_key_file_set_integer (out, sprofgroupname, "presence", + gst_encoding_profile_get_presence (sprof)); + + if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) { + GstEncodingVideoProfile *vp = (GstEncodingVideoProfile *) sprof; + + g_key_file_set_integer (out, sprofgroupname, "pass", + gst_encoding_video_profile_get_pass (vp)); + g_key_file_set_boolean (out, sprofgroupname, "variableframerate", + gst_encoding_video_profile_get_variableframerate (vp)); + } + + g_free (sprofgroupname); + return TRUE; +} + +/* Serialize the top-level profiles + * Note: They don't have to be containerprofiles */ +static gboolean +serialize_encoding_profile (GKeyFile * out, GstEncodingProfile * prof) +{ + gchar *profgroupname; + const GList *tmp; + guint i; + const gchar *profname, *profdesc, *profpreset, *proftype; + const GstCaps *profformat, *profrestriction; + + profname = gst_encoding_profile_get_name (prof); + profdesc = gst_encoding_profile_get_description (prof); + profformat = gst_encoding_profile_get_format (prof); + profpreset = gst_encoding_profile_get_preset (prof); + proftype = gst_encoding_profile_get_type_nick (prof); + profrestriction = gst_encoding_profile_get_restriction (prof); + + profgroupname = g_strdup_printf ("profile-%s", profname); + + g_key_file_set_string (out, profgroupname, "name", profname); + + g_key_file_set_value (out, profgroupname, "type", + gst_encoding_profile_get_type_nick (prof)); + + if (profdesc) + g_key_file_set_locale_string (out, profgroupname, "description", + setlocale (LC_ALL, NULL), profdesc); + if (profformat) { + gchar *tmpc = gst_caps_to_string (profformat); + g_key_file_set_string (out, profgroupname, "format", tmpc); + g_free (tmpc); + } + if (profpreset) + g_key_file_set_string (out, profgroupname, "preset", profpreset); + + /* stream profiles */ + if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) { + for (tmp = + gst_encoding_container_profile_get_profiles + (GST_ENCODING_CONTAINER_PROFILE (prof)), i = 0; tmp; + tmp = tmp->next, i++) { + GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data; + + if (!serialize_stream_profiles (out, sprof, profname, i)) + return FALSE; + } + } + g_free (profgroupname); + return TRUE; +} + +static gboolean +serialize_target (GKeyFile * out, GstEncodingTarget * target) +{ + GList *tmp; + + g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "name", target->name); + g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "category", + target->category); + g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "description", + target->description); + + for (tmp = target->profiles; tmp; tmp = tmp->next) { + GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data; + if (!serialize_encoding_profile (out, prof)) + return FALSE; + } + + return TRUE; +} + +/** + * parse_encoding_profile: + * @in: a #GKeyFile + * @parentprofilename: the parent profile name (including 'profile-' or 'streamprofile-' header) + * @profilename: the profile name group to parse + * @nbgroups: the number of top-level groups + * @groups: the top-level groups + */ +static GstEncodingProfile * +parse_encoding_profile (GKeyFile * in, gchar * parentprofilename, + gchar * profilename, gsize nbgroups, gchar ** groups) +{ + GstEncodingProfile *sprof = NULL; + gchar **parent; + gchar *proftype, *format, *preset, *restriction, *pname, *description; + GstCaps *formatcaps = NULL; + GstCaps *restrictioncaps = NULL; + gboolean variableframerate; + gint pass, presence; + gsize i, nbencprofiles; + + GST_DEBUG ("parentprofilename : %s , profilename : %s", + parentprofilename, profilename); + + if (parentprofilename) { + gboolean found = FALSE; + + parent = + g_key_file_get_string_list (in, profilename, "parent", + &nbencprofiles, NULL); + if (!parent || !nbencprofiles) { + return NULL; + } + + /* Check if this streamprofile is used in */ + for (i = 0; i < nbencprofiles; i++) { + if (!g_strcmp0 (parent[i], parentprofilename)) { + found = TRUE; + break; + } + } + g_strfreev (parent); + + if (!found) { + GST_DEBUG ("Stream profile '%s' isn't used in profile '%s'", + profilename, parentprofilename); + return NULL; + } + } + + pname = g_key_file_get_value (in, profilename, "name", NULL); + + /* First try to get localized description */ + description = + g_key_file_get_locale_string (in, profilename, "description", + setlocale (LC_ALL, NULL), NULL); + if (description == NULL) + description = g_key_file_get_value (in, profilename, "description", NULL); + + /* Parse the remaining fields */ + proftype = g_key_file_get_value (in, profilename, "type", NULL); + if (!proftype) { + GST_WARNING ("Missing 'type' field for streamprofile %s", profilename); + return NULL; + } + + format = g_key_file_get_value (in, profilename, "format", NULL); + if (format) { + formatcaps = gst_caps_from_string (format); + g_free (format); + } + + preset = g_key_file_get_value (in, profilename, "preset", NULL); + + restriction = g_key_file_get_value (in, profilename, "restriction", NULL); + if (restriction) { + restrictioncaps = gst_caps_from_string (restriction); + g_free (restriction); + } + + presence = g_key_file_get_integer (in, profilename, "presence", NULL); + pass = g_key_file_get_integer (in, profilename, "pass", NULL); + variableframerate = + g_key_file_get_boolean (in, profilename, "variableframerate", NULL); + + /* Build the streamprofile ! */ + if (!g_strcmp0 (proftype, "container")) { + GstEncodingProfile *pprof; + + sprof = + (GstEncodingProfile *) gst_encoding_container_profile_new (pname, + description, formatcaps, preset); + /* Now look for the stream profiles */ + for (i = 0; i < nbgroups; i++) { + if (!g_ascii_strncasecmp (groups[i], "streamprofile-", 13)) { + pprof = parse_encoding_profile (in, pname, groups[i], nbgroups, groups); + if (pprof) { + gst_encoding_container_profile_add_profile ( + (GstEncodingContainerProfile *) sprof, pprof); + } + } + } + } else if (!g_strcmp0 (proftype, "video")) { + sprof = + (GstEncodingProfile *) gst_encoding_video_profile_new (formatcaps, + preset, restrictioncaps, presence); + gst_encoding_video_profile_set_variableframerate ((GstEncodingVideoProfile + *) sprof, variableframerate); + gst_encoding_video_profile_set_pass ((GstEncodingVideoProfile *) sprof, + pass); + } else if (!g_strcmp0 (proftype, "audio")) { + sprof = + (GstEncodingProfile *) gst_encoding_audio_profile_new (formatcaps, + preset, restrictioncaps, presence); + } else + GST_ERROR ("Unknown profile format '%s'", proftype); + + if (restrictioncaps) + gst_caps_unref (restrictioncaps); + if (formatcaps) + gst_caps_unref (formatcaps); + + if (pname) + g_free (pname); + if (description) + g_free (description); + if (preset) + g_free (preset); + if (proftype) + g_free (proftype); + + return sprof; +} + +static GstEncodingTarget * +parse_keyfile (GKeyFile * in, gchar * targetname, gchar * categoryname, + gchar * description) +{ + GstEncodingTarget *res = NULL; + GstEncodingProfile *prof; + gchar **groups; + gsize i, nbgroups; + + res = gst_encoding_target_new (targetname, categoryname, description, NULL); + + /* Figure out the various profiles */ + groups = g_key_file_get_groups (in, &nbgroups); + for (i = 0; i < nbgroups; i++) { + if (!g_ascii_strncasecmp (groups[i], "profile-", 8)) { + prof = parse_encoding_profile (in, NULL, groups[i], nbgroups, groups); + if (prof) + gst_encoding_target_add_profile (res, prof); + } + } + + g_strfreev (groups); + + if (targetname) + g_free (targetname); + if (categoryname) + g_free (categoryname); + if (description) + g_free (description); + + return res; +} + +static GKeyFile * +load_file_and_read_header (const gchar * path, gchar ** targetname, + gchar ** categoryname, gchar ** description, GError ** error) +{ + GKeyFile *in; + gboolean res; + + in = g_key_file_new (); + + GST_DEBUG ("path:%s", path); + + res = + g_key_file_load_from_file (in, path, + G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, error); + if (!res || error != NULL) + goto load_error; + + *targetname = + g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "name", error); + if (!*targetname) + goto empty_name; + + *categoryname = + g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "category", NULL); + *description = + g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "description", + NULL); + + return in; + +load_error: + { + GST_WARNING ("Unable to read GstEncodingTarget file %s: %s", + path, (*error)->message); + g_key_file_free (in); + return NULL; + } + +empty_name: + { + GST_WARNING ("Wrong header in file %s: %s", path, (*error)->message); + g_key_file_free (in); + return NULL; + } +} + +/** + * gst_encoding_target_load_from: + * @path: The file to load the #GstEncodingTarget from + * @error: If an error occured, this field will be filled in. + * + * Opens the provided file and returns the contained #GstEncodingTarget. + * + * Since: 0.10.32 + * + * Returns: The #GstEncodingTarget contained in the file, else %NULL + */ + +GstEncodingTarget * +gst_encoding_target_load_from (const gchar * path, GError ** error) +{ + GKeyFile *in; + gchar *targetname, *categoryname, *description; + GstEncodingTarget *res = NULL; + + in = load_file_and_read_header (path, &targetname, &categoryname, + &description, error); + if (!in) + goto beach; + + res = parse_keyfile (in, targetname, categoryname, description); + + g_key_file_free (in); + +beach: + return res; +} + +/** + * gst_encoding_target_load: + * @name: the name of the #GstEncodingTarget to load. + * @error: If an error occured, this field will be filled in. + * + * Searches for the #GstEncodingTarget with the given name, loads it + * and returns it. + * + * Warning: NOT IMPLEMENTED. + * + * Since: 0.10.32 + * + * Returns: The #GstEncodingTarget if available, else %NULL + */ + +GstEncodingTarget * +gst_encoding_target_load (const gchar * name, GError ** error) +{ + /* FIXME : IMPLEMENT */ + return NULL; +} + +/** + * gst_encoding_target_save: + * @target: a #GstEncodingTarget + * @error: If an error occured, this field will be filled in. + * + * Saves the @target to the default location. + * + * Warning: NOT IMPLEMENTED. + * + * Since: 0.10.32 + * + * Returns: %TRUE if the target was correctly saved, else %FALSE. + **/ + +gboolean +gst_encoding_target_save (GstEncodingTarget * target, GError ** error) +{ + g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE); + + /* FIXME : IMPLEMENT */ + return FALSE; +} + +/** + * gst_encoding_target_save_to: + * @target: a #GstEncodingTarget + * @path: the location to store the @target at. + * @error: If an error occured, this field will be filled in. + * + * Saves the @target to the provided location. + * + * Since: 0.10.32 + * + * Returns: %TRUE if the target was correctly saved, else %FALSE. + **/ + +gboolean +gst_encoding_target_save_to (GstEncodingTarget * target, const gchar * path, + GError ** error) +{ + GKeyFile *out; + gchar *data; + gsize data_size; + + g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + /* FIXME : Check path is valid and writable + * FIXME : Strip out profiles already present in system target */ + + /* Get unique name... */ + + /* Create output GKeyFile */ + out = g_key_file_new (); + + if (!serialize_target (out, target)) + goto serialize_failure; + + if (!(data = g_key_file_to_data (out, &data_size, error))) + goto convert_failed; + + if (!g_file_set_contents (path, data, data_size, error)) + goto write_failed; + + g_key_file_free (out); + g_free (data); + + return TRUE; + +serialize_failure: + { + GST_ERROR ("Failure serializing target"); + g_key_file_free (out); + return FALSE; + } + +convert_failed: + { + GST_ERROR ("Failure converting keyfile: %s", (*error)->message); + g_key_file_free (out); + g_free (data); + return FALSE; + } + +write_failed: + { + GST_ERROR ("Unable to write file %s: %s", path, (*error)->message); + g_key_file_free (out); + g_free (data); + return FALSE; + } +} diff --git a/gst-libs/gst/pbutils/encoding-target.h b/gst-libs/gst/pbutils/encoding-target.h new file mode 100644 index 0000000000..c23f526609 --- /dev/null +++ b/gst-libs/gst/pbutils/encoding-target.h @@ -0,0 +1,104 @@ +/* GStreamer encoding profile registry + * Copyright (C) 2010 Edward Hervey + * (C) 2010 Nokia Corporation + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_PROFILE_REGISTRY_H__ +#define __GST_PROFILE_REGISTRY_H__ + +#include + +G_BEGIN_DECLS + + +/* FIXME/UNKNOWNS + * + * Should encoding categories be well-known strings/quarks ? + * + */ + +#define GST_ENCODING_CATEGORY_DEVICE "device" +#define GST_ENCODING_CATEGORY_ONLINE_SERVICE "online-service" +#define GST_ENCODING_CATEGORY_STORAGE_EDITING "storage-editing" +#define GST_ENCODING_CATEGORY_CAPTURE "capture" + +/** + * GstEncodingTarget: + * + * Collection of #GstEncodingProfile for a specific target or use-case. + * + * Since: 0.10.32 + */ +#define GST_TYPE_ENCODING_TARGET \ + (gst_encoding_target_get_type ()) +#define GST_ENCODING_TARGET(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODING_TARGET, GstEncodingTarget)) +#define GST_IS_ENCODING_TARGET(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODING_TARGET)) + +typedef struct _GstEncodingTarget GstEncodingTarget; +typedef GstMiniObjectClass GstEncodingTargetClass; + +GType gst_encoding_target_get_type (void); + +/** + * gst_encoding_target_unref: + * @target: a #GstEncodingTarget + * + * Decreases the reference count of the @target, possibly freeing it. + * + * Since: 0.10.32 + */ +#define gst_encoding_target_unref(target) \ + (gst_mini_object_unref ((GstMiniObject*) target)) + +/** + * gst_encoding_target_ref: + * @target: a #GstEncodingTarget + * + * Increases the reference count of the @target. + * + * Since: 0.10.32 + */ +#define gst_encoding_target_ref(target) \ + (gst_mini_object_ref ((GstMiniObject*) target)) + +GstEncodingTarget * +gst_encoding_target_new (const gchar *name, const gchar *category, + const gchar *description, const GList *profiles); +const gchar *gst_encoding_target_get_name (GstEncodingTarget *target); +const gchar *gst_encoding_target_get_category (GstEncodingTarget *target); +const gchar *gst_encoding_target_get_description (GstEncodingTarget *target); +const GList *gst_encoding_target_get_profiles (GstEncodingTarget *target); + +gboolean +gst_encoding_target_add_profile (GstEncodingTarget *target, GstEncodingProfile *profile); + +gboolean gst_encoding_target_save (GstEncodingTarget *target, + GError **error); +gboolean gst_encoding_target_save_to (GstEncodingTarget *target, + const gchar *path, + GError **error); +GstEncodingTarget *gst_encoding_target_load (const gchar *name, + GError **error); +GstEncodingTarget *gst_encoding_target_load_from (const gchar *path, + GError **error); + +G_END_DECLS + +#endif /* __GST_PROFILE_REGISTRY_H__ */ diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 15f2ec82f9..59ba7403f8 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -127,6 +127,7 @@ check_PROGRAMS = \ libs/navigation \ libs/netbuffer \ libs/pbutils \ + libs/profile \ libs/rtp \ libs/tag \ libs/video \ @@ -238,6 +239,12 @@ libs_pbutils_LDADD = \ $(top_builddir)/gst-libs/gst/video/libgstvideo-@GST_MAJORMINOR@.la \ $(GST_BASE_LIBS) $(LDADD) +libs_profile_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(AM_CFLAGS) +libs_profile_LDADD = \ + $(top_builddir)/gst-libs/gst/pbutils/libgstpbutils-@GST_MAJORMINOR@.la $(LDADD) + elements_appsink_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(AM_CFLAGS) diff --git a/tests/check/libs/.gitignore b/tests/check/libs/.gitignore index cd372588c4..326c47aa4e 100644 --- a/tests/check/libs/.gitignore +++ b/tests/check/libs/.gitignore @@ -6,6 +6,7 @@ mixer navigation netbuffer pbutils +profile rtp tag utils diff --git a/tests/check/libs/profile.c b/tests/check/libs/profile.c new file mode 100644 index 0000000000..a5640a646a --- /dev/null +++ b/tests/check/libs/profile.c @@ -0,0 +1,454 @@ +/* GStreamer unit test for gstprofile + * + * Copyright (C) <2009> Edward Hervey + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* #include */ +#include +#include +#include +#include + +#include +#include + +#define CHECK_PROFILE(profile, name, description, format, preset, presence, restriction) \ + { \ + fail_if(profile == NULL); \ + fail_unless_equals_string (gst_encoding_profile_get_name (profile), name); \ + fail_unless_equals_string (gst_encoding_profile_get_description (profile), description); \ + fail_unless (gst_caps_is_equal (gst_encoding_profile_get_format (profile), format)); \ + fail_unless_equals_string (gst_encoding_profile_get_preset (profile), preset); \ + fail_unless_equals_int (gst_encoding_profile_get_presence (profile), presence); \ + fail_unless (gst_caps_is_equal (gst_encoding_profile_get_restriction (profile), restriction)); \ + } + +GST_START_TEST (test_profile_creation) +{ + GstEncodingProfile *encprof; + GstEncodingAudioProfile *audioprof; + GstEncodingVideoProfile *videoprof; + GstCaps *ogg, *vorbis, *theora; + GstCaps *test1, *test2; + + ogg = gst_caps_new_simple ("application/ogg", NULL); + vorbis = gst_caps_new_simple ("audio/x-vorbis", NULL); + theora = gst_caps_new_simple ("video/x-theora", NULL); + + encprof = (GstEncodingProfile *) gst_encoding_container_profile_new ((gchar *) + "ogg-theora-vorbis", "dumb-profile", ogg, (gchar *) "dumb-preset"); + CHECK_PROFILE (encprof, "ogg-theora-vorbis", "dumb-profile", ogg, + "dumb-preset", 0, NULL); + + audioprof = gst_encoding_audio_profile_new (vorbis, (gchar *) "HQ", NULL, 0); + CHECK_PROFILE ((GstEncodingProfile *) audioprof, NULL, NULL, vorbis, "HQ", 0, + NULL); + + videoprof = gst_encoding_video_profile_new (theora, (gchar *) "HQ", NULL, 0); + CHECK_PROFILE ((GstEncodingProfile *) videoprof, NULL, NULL, theora, "HQ", + 0, NULL); + + fail_unless (gst_encoding_container_profile_add_profile ( + (GstEncodingContainerProfile *) encprof, + (GstEncodingProfile *) audioprof)); + fail_unless (gst_encoding_container_profile_add_profile ( + (GstEncodingContainerProfile *) encprof, + (GstEncodingProfile *) videoprof)); + + /* Test caps */ + test1 = gst_caps_from_string ("video/x-theora; audio/x-vorbis"); + test2 = gst_encoding_profile_get_output_caps (encprof); + fail_unless (gst_caps_is_equal (test1, test2)); + gst_caps_unref (test1); + gst_caps_unref (test2); + + gst_encoding_profile_unref (encprof); + gst_caps_unref (ogg); + gst_caps_unref (theora); + gst_caps_unref (vorbis); +} + +GST_END_TEST; + + +GST_START_TEST (test_profile_output_caps) +{ + GstEncodingProfile *sprof; + GstCaps *vorbis; + GstCaps *out, *restriction, *test1; + + vorbis = gst_caps_new_simple ("audio/x-vorbis", NULL); + + /* Simple case, no restriction */ + sprof = (GstEncodingProfile *) + gst_encoding_audio_profile_new (vorbis, NULL, NULL, 0); + fail_if (sprof == NULL); + + out = gst_encoding_profile_get_output_caps (sprof); + fail_if (out == NULL); + fail_unless (gst_caps_is_equal (out, vorbis)); + gst_caps_unref (out); + gst_encoding_profile_unref (sprof); + + /* One simple restriction */ + restriction = gst_caps_from_string ("audio/x-raw-int,channels=2,rate=44100"); + test1 = gst_caps_from_string ("audio/x-vorbis,channels=2,rate=44100"); + fail_if (restriction == NULL); + + sprof = (GstEncodingProfile *) + gst_encoding_audio_profile_new (vorbis, NULL, restriction, 0); + fail_if (sprof == NULL); + + out = gst_encoding_profile_get_output_caps (sprof); + fail_if (out == NULL); + GST_DEBUG ("got caps %" GST_PTR_FORMAT, out); + fail_unless (gst_caps_is_equal (out, test1)); + gst_caps_unref (out); + gst_caps_unref (restriction); + gst_caps_unref (test1); + gst_encoding_profile_unref (sprof); + + gst_caps_unref (vorbis); +} + +GST_END_TEST; + +GST_START_TEST (test_containerless_profile) +{ + GstEncodingProfile *encprof; + GstEncodingAudioProfile *audioprof; + GstCaps *container = NULL, *vorbis; + GstCaps *test1, *test2; + + vorbis = gst_caps_new_simple ("audio/x-vorbis", NULL); + + GST_DEBUG ("Creating container profile without any caps"); + encprof = (GstEncodingProfile *) gst_encoding_container_profile_new ((gchar *) + "container-vorbis", "dumb-profile", container, (gchar *) "dumb-preset"); + CHECK_PROFILE (encprof, "container-vorbis", "dumb-profile", NULL, + "dumb-preset", 0, 0); + + GST_DEBUG ("Creating audio profile"); + audioprof = gst_encoding_audio_profile_new (vorbis, (gchar *) "HQ", NULL, 0); + CHECK_PROFILE ((GstEncodingProfile *) audioprof, NULL, NULL, vorbis, "HQ", 0, + 0); + + GST_DEBUG ("Adding audio profile to container"); + /* We can add one stream profile to container-less profiles.. */ + fail_unless (gst_encoding_container_profile_add_profile ( + (GstEncodingContainerProfile *) encprof, + (GstEncodingProfile *) audioprof)); + GST_DEBUG ("Adding audio profile to container a second time (should fail)"); + /* .. but not two */ + fail_if (gst_encoding_container_profile_add_profile ( + (GstEncodingContainerProfile *) encprof, + (GstEncodingProfile *) audioprof)); + + GST_DEBUG ("Checking caps"); + /* Test caps */ + test1 = gst_caps_from_string ("audio/x-vorbis"); + test2 = gst_encoding_profile_get_output_caps (encprof); + fail_unless (gst_caps_is_equal (test1, test2)); + gst_caps_unref (test1); + gst_caps_unref (test2); + + gst_encoding_profile_unref (encprof); + gst_caps_unref (vorbis); +} + +GST_END_TEST; + +static GstEncodingTarget * +create_saveload_target (void) +{ + GstEncodingTarget *target; + GstEncodingProfile *profile, *sprof; + GstCaps *caps, *caps2; + + GST_DEBUG ("Creating target"); + + target = gst_encoding_target_new ("myponytarget", "herding", + "Plenty of pony glitter profiles", NULL); + caps = gst_caps_from_string ("animal/x-pony"); + profile = + (GstEncodingProfile *) gst_encoding_container_profile_new ("pony", + "I don't want a description !", caps, NULL); + gst_caps_unref (caps); + gst_encoding_target_add_profile (target, profile); + + caps = gst_caps_from_string ("audio/x-pony-song,pretty=True"); + caps2 = gst_caps_from_string ("audio/x-raw-int,channels=1,rate=44100"); + sprof = + (GstEncodingProfile *) gst_encoding_audio_profile_new (caps, NULL, caps2, + 1); + gst_encoding_container_profile_add_profile ((GstEncodingContainerProfile *) + profile, sprof); + gst_caps_unref (caps); + gst_caps_unref (caps2); + + caps = gst_caps_from_string ("video/x-glitter,sparkling=True"); + caps2 = + gst_caps_from_string + ("video/x-raw-yuv,width=640,height=480,framerate=15/1"); + sprof = (GstEncodingProfile *) + gst_encoding_video_profile_new (caps, "seriously glittery", caps2, 0); + gst_encoding_video_profile_set_variableframerate ((GstEncodingVideoProfile *) + sprof, TRUE); + gst_encoding_container_profile_add_profile ((GstEncodingContainerProfile *) + profile, sprof); + gst_caps_unref (caps); + gst_caps_unref (caps2); + + return target; +} + +GST_START_TEST (test_saving_profile) +{ + GstEncodingTarget *orig, *loaded = NULL; + GstEncodingProfile *proforig, *profloaded; + gchar *profile_file_name; + + /* Create and store a target */ + profile_file_name = g_build_filename (g_get_home_dir (), ".gstreamer-0.10", + "profile", "TestProfile2.profile", NULL); + orig = create_saveload_target (); + GST_DEBUG ("Saving target to '%s'", profile_file_name); + fail_unless (gst_encoding_target_save_to (orig, profile_file_name, NULL)); + + /* Check we can load it */ + GST_DEBUG ("Loading target from '%s'", profile_file_name); + loaded = gst_encoding_target_load_from (profile_file_name, NULL); + fail_unless (loaded != NULL); + g_free (profile_file_name); + + GST_DEBUG ("Checking targets are equal"); + /* Check targets are identical */ + /* 1. at the target level */ + fail_unless_equals_string (gst_encoding_target_get_name (orig), + gst_encoding_target_get_name (loaded)); + fail_unless_equals_string (gst_encoding_target_get_category (orig), + gst_encoding_target_get_category (loaded)); + fail_unless_equals_string (gst_encoding_target_get_description (orig), + gst_encoding_target_get_description (loaded)); + fail_unless_equals_int (g_list_length ((GList *) + gst_encoding_target_get_profiles (loaded)), 1); + + /* 2. at the profile level */ + profloaded = + (GstEncodingProfile *) gst_encoding_target_get_profiles (loaded)->data; + proforig = + (GstEncodingProfile *) gst_encoding_target_get_profiles (orig)->data; + + fail_unless_equals_int (G_TYPE_FROM_INSTANCE (profloaded), + G_TYPE_FROM_INSTANCE (proforig)); + GST_DEBUG ("Comparing loaded:%p to original:%p", profloaded, proforig); + fail_unless (gst_encoding_profile_is_equal (profloaded, proforig)); + + gst_encoding_target_unref (orig); + gst_encoding_target_unref (loaded); +} + +GST_END_TEST; + +GST_START_TEST (test_loading_profile) +{ + GstEncodingTarget *target; + gchar *profile_file_name; + GstEncodingProfile *prof; + GstCaps *tmpcaps, *tmpcaps2; + GstEncodingProfile *sprof1, *sprof2; + + profile_file_name = g_build_filename (g_get_home_dir (), ".gstreamer-0.10", + "profile", "TestProfile.profile", NULL); + + GST_DEBUG ("Loading target from '%s'", profile_file_name); + target = gst_encoding_target_load_from (profile_file_name, NULL); + g_free (profile_file_name); + fail_unless (target != NULL); + + GST_DEBUG ("Checking the target properties"); + /* Check the target */ + fail_unless_equals_string (gst_encoding_target_get_name (target), + "myponytarget"); + fail_unless_equals_string (gst_encoding_target_get_category (target), + "herding"); + fail_unless_equals_string (gst_encoding_target_get_description (target), + "Plenty of pony glitter profiles"); + + GST_DEBUG ("Checking the number of profiles the target contains"); + fail_unless_equals_int (g_list_length ((GList *) + gst_encoding_target_get_profiles (target)), 1); + + + GST_DEBUG ("Checking the container profile"); + /* Check the profile */ + prof = (GstEncodingProfile *) gst_encoding_target_get_profiles (target)->data; + tmpcaps = gst_caps_from_string ("animal/x-pony"); + CHECK_PROFILE (prof, "pony", "I don't want a description !", tmpcaps, NULL, 0, + 0); + gst_caps_unref (tmpcaps); + + GST_DEBUG ("Checking the container profile has 2 stream profiles"); + /* Check the stream profiles */ + fail_unless_equals_int (g_list_length ((GList *) + gst_encoding_container_profile_get_profiles ( + (GstEncodingContainerProfile *) prof)), 2); + + GST_DEBUG ("Checking the container profile has the audio/x-pony-song stream"); + tmpcaps = gst_caps_from_string ("audio/x-pony-song,pretty=True"); + tmpcaps2 = gst_caps_from_string ("audio/x-raw-int,channels=1,rate=44100"); + sprof1 = + (GstEncodingProfile *) gst_encoding_audio_profile_new (tmpcaps, NULL, + tmpcaps2, 1); + fail_unless (gst_encoding_container_profile_contains_profile ( + (GstEncodingContainerProfile *) prof, sprof1)); + gst_encoding_profile_unref (sprof1); + gst_caps_unref (tmpcaps); + gst_caps_unref (tmpcaps2); + + GST_DEBUG ("Checking the container profile has the video//x-glitter stream"); + tmpcaps = gst_caps_from_string ("video/x-glitter,sparkling=True"); + tmpcaps2 = + gst_caps_from_string + ("video/x-raw-yuv,width=640,height=480,framerate=15/1"); + sprof2 = (GstEncodingProfile *) + gst_encoding_video_profile_new (tmpcaps, "seriously glittery", tmpcaps2, + 0); + gst_encoding_video_profile_set_variableframerate ((GstEncodingVideoProfile *) + sprof2, TRUE); + fail_unless (gst_encoding_container_profile_contains_profile ( + (GstEncodingContainerProfile *) prof, sprof2)); + gst_encoding_profile_unref (sprof2); + gst_caps_unref (tmpcaps); + gst_caps_unref (tmpcaps2); + + gst_encoding_target_unref (target); +} + +GST_END_TEST; + +static const gchar *profile_string = "\ +[_gstencodingtarget_]\n\ +name=myponytarget\n\ +category=herding\n\ +description=Plenty of pony glitter profiles\n\ +\n\ +[profile-pony1]\n\ +name=pony\n\ +type=container\n\ +description=I don't want a description !\n\ +format=animal/x-pony\n\ +\n\ +[streamprofile-pony11]\n\ +parent=pony\n\ +type=audio\n\ +format=audio/x-pony-song,pretty=True\n\ +restriction=audio/x-raw-int,channels=1,rate=44100\n\ +presence=1\n\ +\n\ +[streamprofile-pony12]\n\ +parent=pony\n\ +type=video\n\ +preset=seriously glittery\n\ +format=video/x-glitter,sparkling=True\n\ +restriction=video/x-raw-yuv,width=640,height=480,framerate=15/1\n\ +presence=0\n\ +variableframerate=true\n\ +"; + +static void +remove_profile_file (void) +{ + gchar *profile_file_name; + + profile_file_name = g_build_filename (g_get_home_dir (), ".gstreamer-0.10", + "profile", "TestProfile.profile", NULL); + g_unlink (profile_file_name); + g_free (profile_file_name); + profile_file_name = g_build_filename (g_get_home_dir (), ".gstreamer-0.10", + "profile", "TestProfile2.profile", NULL); + g_unlink (profile_file_name); + g_free (profile_file_name); +} + +static void +create_profile_file (void) +{ + gchar *profile_file_name; + gchar *profile_dir; + GError *error = NULL; + + profile_dir = + g_build_filename (g_get_home_dir (), ".gstreamer-0.10", "profile", NULL); + profile_file_name = + g_build_filename (g_get_home_dir (), ".gstreamer-0.10", "profile", + "TestProfile.profile", NULL); + g_mkdir_with_parents (profile_dir, S_IRUSR | S_IWUSR | S_IXUSR); + if (!g_file_set_contents (profile_file_name, profile_string, + strlen (profile_string), &error)) + GST_WARNING ("Couldn't write contents to file : %s", error->message); + g_free (profile_dir); + g_free (profile_file_name); +} + +static void +test_setup (void) +{ + create_profile_file (); +} + +static void +test_teardown (void) +{ + remove_profile_file (); +} + + +static Suite * +profile_suite (void) +{ + Suite *s = suite_create ("profile support library"); + TCase *tc_chain = tcase_create ("general"); + gboolean can_write; + gchar *gst_dir; + + /* cehck if we can create profiles */ + gst_dir = g_build_filename (g_get_home_dir (), ".gstreamer-0.10", NULL); + can_write = (g_access (gst_dir, R_OK | W_OK | X_OK) == 0); + g_free (gst_dir); + + suite_add_tcase (s, tc_chain); + + tcase_add_test (tc_chain, test_profile_creation); + tcase_add_test (tc_chain, test_profile_output_caps); + tcase_add_test (tc_chain, test_containerless_profile); + if (can_write) { + tcase_add_test (tc_chain, test_loading_profile); + tcase_add_test (tc_chain, test_saving_profile); + } + + tcase_add_unchecked_fixture (tc_chain, test_setup, test_teardown); + + return s; +} + +GST_CHECK_MAIN (profile); diff --git a/win32/common/libgstpbutils.def b/win32/common/libgstpbutils.def index 34c79dc635..6f43ee4e86 100644 --- a/win32/common/libgstpbutils.def +++ b/win32/common/libgstpbutils.def @@ -58,6 +58,46 @@ EXPORTS gst_discoverer_video_info_get_width gst_discoverer_video_info_is_image gst_discoverer_video_info_is_interlaced + gst_encoding_audio_profile_get_type + gst_encoding_audio_profile_new + gst_encoding_container_profile_add_profile + gst_encoding_container_profile_contains_profile + gst_encoding_container_profile_get_profiles + gst_encoding_container_profile_get_type + gst_encoding_container_profile_new + gst_encoding_profile_get_description + gst_encoding_profile_get_format + gst_encoding_profile_get_name + gst_encoding_profile_get_output_caps + gst_encoding_profile_get_presence + gst_encoding_profile_get_preset + gst_encoding_profile_get_restriction + gst_encoding_profile_get_type + gst_encoding_profile_get_type_nick + gst_encoding_profile_is_equal + gst_encoding_profile_set_description + gst_encoding_profile_set_format + gst_encoding_profile_set_name + gst_encoding_profile_set_presence + gst_encoding_profile_set_preset + gst_encoding_profile_set_restriction + gst_encoding_target_add_profile + gst_encoding_target_get_category + gst_encoding_target_get_description + gst_encoding_target_get_name + gst_encoding_target_get_profiles + gst_encoding_target_get_type + gst_encoding_target_load + gst_encoding_target_load_from + gst_encoding_target_new + gst_encoding_target_save + gst_encoding_target_save_to + gst_encoding_video_profile_get_pass + gst_encoding_video_profile_get_type + gst_encoding_video_profile_get_variableframerate + gst_encoding_video_profile_new + gst_encoding_video_profile_set_pass + gst_encoding_video_profile_set_variableframerate gst_install_plugins_async gst_install_plugins_context_free gst_install_plugins_context_get_type