Skip to content
New issue

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

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gstreamer encoder #9

Merged
merged 28 commits into from Dec 24, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6730050
Initial implementation of GStreamer encoder.
smimram Oct 27, 2012
f178d5f
Merge branch 'gstreamer' into gstreamer_encoder
smimram Oct 27, 2012
ca6ecdb
Use some mutexify.
toots Oct 30, 2012
3daa94b
fix typo.
toots Oct 30, 2012
8e31d8a
Done more administrative stuff.
toots Nov 9, 2012
6297e39
Merge branch 'master' into gstreamer_encoder
toots Nov 9, 2012
8bcd3c9
Cleanup some debug stuff.
toots Nov 12, 2012
82e3961
Speard video frames presentation over the frame duration.
toots Nov 12, 2012
f90f0ea
More appropriate video encoding loop. Video rate not fixed yet, tho..
toots Nov 18, 2012
1dec9dd
Use _ to split nano.
toots Nov 18, 2012
ef0eebb
Works much better with mpegts muxer :)
toots Nov 18, 2012
a26c1fe
Allow to have only audio or only video and change the muxer. Does not…
toots Nov 22, 2012
ef50490
NEVER TRUST YOUR COMPILER! :-)
toots Nov 24, 2012
c16ee1e
Emit end_of_stream on stop.
smimram Nov 24, 2012
f74fca3
Change the way we handle end of streams.
toots Nov 24, 2012
31c99b5
Remove unrelated camlp4 diff..
toots Nov 24, 2012
1958206
Start stop loop only if some samples have been decoded..
toots Nov 24, 2012
8e52e28
Always call state_null on stop tho.
toots Nov 24, 2012
a73fd76
Merge branch 'master' into gstreamer_encoder
toots Nov 24, 2012
118d685
Implement insert_metadata
toots Nov 28, 2012
9b89ee2
Added debug, metadata and pipeline parameters to %gstreamer.
toots Nov 30, 2012
f145e00
Better log/debug semantics.
toots Nov 30, 2012
0cc8ae6
Added encoder documentation!
toots Dec 14, 2012
0fa6141
Cleaned up doc/orig
toots Dec 17, 2012
9e6b1d8
Give encoding_formats some love..
toots Dec 17, 2012
2d5b38a
Merge branch 'master' into gstreamer_encoder
toots Dec 17, 2012
baa5e16
Better wording.
toots Dec 17, 2012
748fb5b
Merge branch 'master' into gstreamer_encoder
toots Dec 17, 2012
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 22 additions & 0 deletions doc/content/encoding_formats.txt
Expand Up @@ -213,6 +213,28 @@ The syntax for the internal AAC+ encoder is:
%aacplus(channels=2, samplerate=44100, bitrate=64) %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 h4. External encoders


For a detailed presentation of external encoders, see "this page":external_encoders.html. For a detailed presentation of external encoders, see "this page":external_encoders.html.
Expand Down
155 changes: 155 additions & 0 deletions doc/content/gstreamer_encoder.txt
@@ -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.
1 change: 1 addition & 0 deletions src/Makefile
Expand Up @@ -247,6 +247,7 @@ $(call conditional_compilation,operators,$(W_GD),operators/video_text_gd.ml)
# GStreamer # GStreamer
$(call conditional_compilation,stream,$(W_GSTREAMER),tools/gstreamer_utils.ml) $(call conditional_compilation,stream,$(W_GSTREAMER),tools/gstreamer_utils.ml)
$(call conditional_compilation,decoders,$(W_GSTREAMER),decoder/gstreamer_decoder.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,operators,$(W_GSTREAMER),operators/video_text_gstreamer.ml)
$(call conditional_compilation,io,$(W_GSTREAMER),io/gstreamer_io.ml) $(call conditional_compilation,io,$(W_GSTREAMER),io/gstreamer_io.ml)


Expand Down
4 changes: 2 additions & 2 deletions src/decoder/gstreamer_decoder.ml
Expand Up @@ -149,7 +149,7 @@ module Make (Generator : Generator.S_Asio) = struct
let _, state, _ = Gstreamer.Element.get_state gst.bin in let _, state, _ = Gstreamer.Element.get_state gst.bin in
if state <> Gstreamer.Element.State_playing then if state <> Gstreamer.Element.State_playing then
failwith "Not in playing state!"; 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 len = String.length b / (2*channels) in
let buf = Audio.create channels len in let buf = Audio.create channels len in
Audio.S16LE.to_audio b 0 buf 0 len; Audio.S16LE.to_audio b 0 buf 0 len;
Expand All @@ -160,7 +160,7 @@ module Make (Generator : Generator.S_Asio) = struct
let _, state, _ = Gstreamer.Element.get_state gst.bin in let _, state, _ = Gstreamer.Element.get_state gst.bin in
if state <> Gstreamer.Element.State_playing then if state <> Gstreamer.Element.State_playing then
failwith "Not in playing state!"; 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 img = Img.make width height b in
let stream = [|img|] in let stream = [|img|] in
Generator.put_video buffer [|stream|] 0 (Array.length stream) Generator.put_video buffer [|stream|] 0 (Array.length stream)
Expand Down
52 changes: 52 additions & 0 deletions src/encoder/encoder.ml
Expand Up @@ -422,6 +422,52 @@ struct


end 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 = module Theora =
struct struct


Expand Down Expand Up @@ -535,6 +581,7 @@ type format =
| AACPlus of AACPlus.t | AACPlus of AACPlus.t
| VoAacEnc of VoAacEnc.t | VoAacEnc of VoAacEnc.t
| External of External.t | External of External.t
| GStreamer of GStreamer.t


let kind_of_format = function let kind_of_format = function
| WAV w -> | WAV w ->
Expand Down Expand Up @@ -576,6 +623,10 @@ let kind_of_format = function
| External e -> | External e ->
{ Frame.audio = e.External.channels ; { Frame.audio = e.External.channels ;
Frame.video = 0 ; Frame.midi = 0 } 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 kind_of_format f =
let k = kind_of_format f in let k = kind_of_format f in
Expand All @@ -592,6 +643,7 @@ let string_of_format = function
| AACPlus w -> AACPlus.to_string w | AACPlus w -> AACPlus.to_string w
| VoAacEnc w -> VoAacEnc.to_string w | VoAacEnc w -> VoAacEnc.to_string w
| External w -> External.to_string w | External w -> External.to_string w
| GStreamer w -> GStreamer.to_string w


(** An encoder, once initialized, is something that consumes (** An encoder, once initialized, is something that consumes
* frames, insert metadata and that you eventually close * frames, insert metadata and that you eventually close
Expand Down