Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Gstreamer encoder #9

Merged
merged 28 commits into from Dec 24, 2012
Commits
Jump to file or symbol
Failed to load files and symbols.
+590 −61
Split
@@ -213,6 +213,28 @@ The syntax for the internal AAC+ encoder is:
%aacplus(channels=2, samplerate=44100, bitrate=64)
%%
+h4. Gstreamer
+
+The @%gstreamer@ encoder can be used to encode streams using the @gstreamer@ multimedia framework.
+This encoder extends liquidsoap with all available GStreamer formats which includes most, if not all,
+formats available to your operating system.
+
+The encoder's parameters are as follows:
+
+%%
+%gstreamer(channels=2,
+ audio="lamemp3enc",
+ has_video=true,
+ video="x264enc",
+ muxer="mpegtsmux",
+ metadata="metadata",
+ log=5,
+ pipeline="")
+%%
+
+Please refer to the "Gstreamer encoder":gstreamer_encoder.html page for a detailed explanation
+of this encoder.
+
h4. External encoders
For a detailed presentation of external encoders, see "this page":external_encoders.html.
@@ -0,0 +1,155 @@
+title: Gstreamer Encoder
+
+h3. The Gstreamer encoder
+
+The @%gstreamer@ encoder can be used to encode streams using the @gstreamer@ multimedia framework.
+This encoder extends liquidsoap with all available GStreamer formats (provided they are
+compatible with liquidsoap's model of streams, see Caveats section below), which includes a
+huge array of encoders.
+
+h4. Presentation
+
+A basic understanding of gstreamer's pipelines and configuration should be expected in order to
+understand the following documentation.
+
+The encoder's parameters are as follows:
+
+%%
+%gstreamer(channels=2,
+ audio="lamemp3enc",
+ has_video=true,
+ video="x264enc",
+ muxer="mpegtsmux",
+ metadata="metadata",
+ log=5,
+ pipeline="")
+%%
+
+Without using the @pipeline@ argument, the @audio@ and @video@ arguments are used to build the
+gstreamer pipeline used to encode. By setting the @log@ parameter to a lower value or by using
+@set("log.level",..)@, you should be able to see some example.
+
+h4. Basic examples
+
+Here are a couple of examples:
+
+An MP3 encoder that expects sources of type @audio=2, video=0, midi=0@:
+
+%%
+% liquidsoap 'output.file(%gstreamer(audio="lamemp3enc",
+ muxer="",
+ video="",
+ log=3),...)'
+(...)
+2012/12/13 19:16:23 [encoder.gstreamer:3] Gstreamer encoder pipeline: appsrc
+ name="audio_src" block=true caps="audio/x-raw,format=S16LE,layout=interleaved,
+ channels=2,rate=44100" format=time ! queue ! audioconvert ! audioresample !
+ lamemp3enc ! appsink name=sink sync=false emit-signals=true
+%%
+
+A x264 encoder that expects sources of type @audio=0, video=1, midi=0@:
+
+%%
+% liquidsoap 'output.file(%gstreamer(audio="",
+ muxer="mpegtsmux",
+ video="x264enc",
+ log=3),...)'
+(...)
+2012/12/13 19:14:43 [encoder.gstreamer:3] Gstreamer encoder pipeline: appsrc
+ name="video_src" block=true caps="video/x-raw,format=RGBA,width=320,height=240,
+ framerate=25/1,pixel-aspect-ratio=1/1" format=time blocksize=307200 ! queue !
+ videoconvert ! videoscale add-borders=true ! videorate ! x264enc !
+ mpegtsmux name=muxer ! appsink name=sink sync=false emit-signals=true
+%%
+
+An MPEG TS encoder that expects sources of type @audio=2, video=1, midi=0@:
+
+%%
+% liquidsoap 'output.file(%gstreamer(audio="lamemp3enc",
+ muxer="mpegtsmux",
+ video="x264enc",
+ log=3),...)'
+(...)
+2012/12/13 19:18:09 [encoder.gstreamer:3] Gstreamer encoder pipeline: appsrc
+ name="audio_src" block=true caps="audio/x-raw,format=S16LE,
+ layout=interleaved,channels=2,rate=44100" format=time ! queue ! audioconvert
+ ! audioresample ! lamemp3enc ! muxer. appsrc name="video_src" block=true
+ caps="video/x-raw,format=RGBA,width=320,height=240,framerate=25/1,
+ pixel-aspect-ratio=1/1" format=time blocksize=307200 ! queue ! videoconvert
+ ! videoscale add-borders=true ! videorate ! x264enc ! muxer. mpegtsmux
+ name=muxer ! appsink name=sink sync=false emit-signals=true
+%%
+
+An ogg/vorbis+theora encoder that expects source of type @audio=1, video=1, midi=0@:
+
+%%
+% liquidsoap 'output.file(%gstreamer(audio="vorbisenc",
+ muxer="oggmux",
+ video="theoraenc",
+ channels=1,
+ log=3),...)'
+(...)
+2012/12/13 19:21:17 [encoder.gstreamer:3] Gstreamer encoder pipeline: appsrc
+ name="audio_src" block=true caps="audio/x-raw,format=S16LE,layout=interleaved,
+ channels=1,rate=44100" format=time ! queue ! audioconvert ! audioresample !
+ vorbisenc ! muxer. appsrc name="video_src" block=true caps="video/x-raw,
+ format=RGBA,width=320,height=240,framerate=25/1,pixel-aspect-ratio=1/1"
+ format=time blocksize=307200 ! queue ! videoconvert ! videoscale add-borders=true
+ ! videorate ! theoraenc ! muxer. oggmux name=muxer ! appsink name=sink
+ sync=false emit-signals=true
+%%
+
+For advanced users, the @pipeline@ argument can be used to override the whole pipeline. For instance:
+
+%%
+% liquidsoap 'output.file(%gstreamer(pipeline="appsrc name=\"audio_src\"
+ block=true caps=\"audio/x-raw,format=S16LE,layout=interleaved,
+ channels=1,rate=44100\" format=time ! lamemp3enc ! appsink name=sink
+ sync=false emit-signals=true",channels=1,log=3),...)'
+(...)
+%%
+
+h4. Content type inference
+
+When starting its sources and outputs, liquidsoap determines the content type of each source (audio, video and midi channels).
+During that process, encoders have to inform liquidsoap what type of sources they are expecting. It works as follows for the @%gstreamer@
+encoder:
+
+* If the @audio@ parameter is a string different than @""@ then the encoder expects a stream with @channels@ audio channels.
+* If the @video@ parameter is a string different than @""@ then the encoder expects a stream with @1@ video channel.
+* If the @pipeline@ parameter is a string different than @""@ then the encoder expects a stream with @channels@ audio channels and a video channels only if @has_video@ is true.
+The @has_video@ parameter is only used when using the @pipeline@ parameter.
+
+h4. Metadata
+
+The @%gstreamer@ encoder tries to also encode metadata attached to the stream. This requires that you specify a pipeline element
+named according to the @metadata@ parameter (default: @"metadata"@) that can be used with GStreamer's @tag_setter@ API. Here are two such examples:
+
+An ogg/vorbis encoder with vorbis tags:
+
+%%
+% liquidsoap 'output.file(%gstreamer(audio="vorbisenc ! vorbistag name='metadata'",
+ muxer="oggmux",
+ video=""),...)'
+%%
+
+An MP3 encoder with id3v2 tags:
+
+%%
+% liquidsoap 'output.file(%gstreamer(audio="lamemp3enc",
+ muxer="id3v2mux",
+ video="",
+ metadata="muxer"),...)'
+%%
+
+In the last example, we tell the @%gstreamer@ encoder that the element for injecting metadata is named
+@"muxer"@ because, for id3v2 tags, the gstreamer muxer element is also the element used to inject metadata
+and the @"muxer"@ name is implicitely added by liquidsoap to the muxer element. You can see that by printing
+out the constructed pipeline, as shown before.
+
+h4. Caveats
+
+When using the @%gstreamer@ encoder, one must think of it as an encoder for an infinite stream. This, in particular,
+means that not all containers (muxers) will work. For instance, the AVI and MP4 containers need to write in their
+header informations that are only known with finite streams, such as the stream total's time and etc.. These containers
+are usually not fit for streaming, which is liquidsoap's main functionality.
View
@@ -247,6 +247,7 @@ $(call conditional_compilation,operators,$(W_GD),operators/video_text_gd.ml)
# GStreamer
$(call conditional_compilation,stream,$(W_GSTREAMER),tools/gstreamer_utils.ml)
$(call conditional_compilation,decoders,$(W_GSTREAMER),decoder/gstreamer_decoder.ml)
+$(call conditional_compilation,encoders,$(W_GSTREAMER),encoder/gstreamer_encoder.ml)
$(call conditional_compilation,operators,$(W_GSTREAMER),operators/video_text_gstreamer.ml)
$(call conditional_compilation,io,$(W_GSTREAMER),io/gstreamer_io.ml)
@@ -149,7 +149,7 @@ module Make (Generator : Generator.S_Asio) = struct
let _, state, _ = Gstreamer.Element.get_state gst.bin in
if state <> Gstreamer.Element.State_playing then
failwith "Not in playing state!";
- let b = Gstreamer.App_sink.pull_buffer_string (get_some gst.audio_sink) in
+ let b = Gstreamer.App_sink.pull_buffer_string (Utils.get_some gst.audio_sink) in
let len = String.length b / (2*channels) in
let buf = Audio.create channels len in
Audio.S16LE.to_audio b 0 buf 0 len;
@@ -160,7 +160,7 @@ module Make (Generator : Generator.S_Asio) = struct
let _, state, _ = Gstreamer.Element.get_state gst.bin in
if state <> Gstreamer.Element.State_playing then
failwith "Not in playing state!";
- let b = Gstreamer.App_sink.pull_buffer_data (get_some gst.video_sink) in
+ let b = Gstreamer.App_sink.pull_buffer_data (Utils.get_some gst.video_sink) in
let img = Img.make width height b in
let stream = [|img|] in
Generator.put_video buffer [|stream|] 0 (Array.length stream)
View
@@ -422,6 +422,52 @@ struct
end
+module GStreamer =
+struct
+ type t = {
+ channels : int;
+ audio : string option;
+ has_video : bool;
+ video : string option;
+ muxer : string option;
+ metadata : string;
+ pipeline : string option;
+ log : int
+ }
+
+ let audio_channels m =
+ if m.audio = None then
+ 0
+ else
+ m.channels
+
+ let video_channels m =
+ if m.video = None || not m.has_video then
+ 0
+ else
+ 1
+
+ let to_string m =
+ let pipeline l name value =
+ Utils.some_or l
+ (Utils.maybe
+ (fun value -> (Printf.sprintf "%s=%S" name value)::l)
+ value)
+ in
+ Printf.sprintf "%%gstreamer(%s,metadata=%S,has_video=%b,%slog=%d)"
+ (String.concat ","
+ (pipeline
+ (pipeline
+ (pipeline [Printf.sprintf "channels=%d" m.channels]
+ "audio" m.audio)
+ "video" m.video)
+ "muxer" m.muxer))
+ m.metadata
+ m.has_video
+ (Utils.some_or "" (Utils.maybe (Printf.sprintf "pipeline=%S,") m.pipeline))
+ m.log
+end
+
module Theora =
struct
@@ -535,6 +581,7 @@ type format =
| AACPlus of AACPlus.t
| VoAacEnc of VoAacEnc.t
| External of External.t
+ | GStreamer of GStreamer.t
let kind_of_format = function
| WAV w ->
@@ -576,6 +623,10 @@ let kind_of_format = function
| External e ->
{ Frame.audio = e.External.channels ;
Frame.video = 0 ; Frame.midi = 0 }
+ | GStreamer e ->
+ { Frame.audio = GStreamer.audio_channels e;
+ Frame.video = GStreamer.video_channels e;
+ Frame.midi = 0 }
let kind_of_format f =
let k = kind_of_format f in
@@ -592,6 +643,7 @@ let string_of_format = function
| AACPlus w -> AACPlus.to_string w
| VoAacEnc w -> VoAacEnc.to_string w
| External w -> External.to_string w
+ | GStreamer w -> GStreamer.to_string w
(** An encoder, once initialized, is something that consumes
* frames, insert metadata and that you eventually close
Oops, something went wrong.