mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
gst/playback/: Add screenshot conversion code from totem.
Original commit message from CVS: * gst/playback/Makefile.am: * gst/playback/gstscreenshot.c: (feed_fakesrc), (save_result), (create_element), (gst_play_frame_conv_convert): * gst/playback/gstscreenshot.h: Add screenshot conversion code from totem. * gst/playback/gstplay-marshal.list: * gst/playback/gstplaybin2.c: (gst_play_marshal_BUFFER__BOXED), (gst_play_bin_class_init), (gst_play_bin_convert_frame), (gst_play_bin_get_property), (no_more_pads_cb), (activate_group): Implement frame property to get a color-unconverted snapshot. Implement convert-frame action signal to get a converted snapshot image. Configure connection speed in uridecodebin. Document some more properties. * gst/playback/gstplaysink.c: (gst_play_sink_class_init), (gen_video_chain), (gen_audio_chain), (gst_play_sink_reconfigure), (gst_play_sink_get_last_frame): * gst/playback/gstplaysink.h: Use last-buffer property of the video sink to get a video snapshot. * tests/examples/seek/seek.c: (shot_cb), (main): Add snapshot button for playbin2 and use the frame property to save the frame as a png in the current directory.
This commit is contained in:
parent
58a9fd3622
commit
81558d6a94
9 changed files with 469 additions and 7 deletions
27
ChangeLog
27
ChangeLog
|
@ -1,3 +1,30 @@
|
|||
2008-02-19 Wim Taymans <wim.taymans@collabora.co.uk>
|
||||
|
||||
* gst/playback/Makefile.am:
|
||||
* gst/playback/gstscreenshot.c: (feed_fakesrc), (save_result),
|
||||
(create_element), (gst_play_frame_conv_convert):
|
||||
* gst/playback/gstscreenshot.h:
|
||||
Add screenshot conversion code from totem.
|
||||
|
||||
* gst/playback/gstplay-marshal.list:
|
||||
* gst/playback/gstplaybin2.c: (gst_play_marshal_BUFFER__BOXED),
|
||||
(gst_play_bin_class_init), (gst_play_bin_convert_frame),
|
||||
(gst_play_bin_get_property), (no_more_pads_cb), (activate_group):
|
||||
Implement frame property to get a color-unconverted snapshot.
|
||||
Implement convert-frame action signal to get a converted snapshot image.
|
||||
Configure connection speed in uridecodebin.
|
||||
Document some more properties.
|
||||
|
||||
* gst/playback/gstplaysink.c: (gst_play_sink_class_init),
|
||||
(gen_video_chain), (gen_audio_chain), (gst_play_sink_reconfigure),
|
||||
(gst_play_sink_get_last_frame):
|
||||
* gst/playback/gstplaysink.h:
|
||||
Use last-buffer property of the video sink to get a video snapshot.
|
||||
|
||||
* tests/examples/seek/seek.c: (shot_cb), (main):
|
||||
Add snapshot button for playbin2 and use the frame property to save the
|
||||
frame as a png in the current directory.
|
||||
|
||||
2008-02-19 Sebastian Dröge <slomo@circular-chaos.org>
|
||||
|
||||
Patch by: Josep Torra Valles <josep at fluendo dot com>
|
||||
|
|
|
@ -17,6 +17,7 @@ libgstplaybin_la_SOURCES = \
|
|||
gstplaybasebin.c \
|
||||
gstplay-enum.c \
|
||||
gstfactorylists.c \
|
||||
gstscreenshot.c \
|
||||
gststreaminfo.c \
|
||||
gststreamselector.c
|
||||
|
||||
|
@ -55,6 +56,7 @@ noinst_HEADERS = \
|
|||
gststreaminfo.h \
|
||||
gstfactorylists.h \
|
||||
gstplay-enum.h \
|
||||
gstscreenshot.h \
|
||||
gststreamselector.h
|
||||
|
||||
noinst_PROGRAMS = test decodetest test2 test3 test4 test5 test6 test7
|
||||
|
|
|
@ -6,3 +6,4 @@ ENUM:OBJECT,OBJECT,BOXED
|
|||
ENUM:OBJECT,OBJECT,OBJECT
|
||||
BOXED:OBJECT,OBJECT,BOXED
|
||||
BOXED:INT
|
||||
OBJECT:BOXED
|
||||
|
|
|
@ -252,6 +252,7 @@
|
|||
#include "gstplay-marshal.h"
|
||||
#include "gstplaysink.h"
|
||||
#include "gstfactorylists.h"
|
||||
#include "gstscreenshot.h"
|
||||
#include "gststreaminfo.h"
|
||||
#include "gststreamselector.h"
|
||||
|
||||
|
@ -338,15 +339,22 @@ struct _GstPlayBinClass
|
|||
{
|
||||
GstPipelineClass parent_class;
|
||||
|
||||
/* notify app that the current uri finished decoding and it is possible to
|
||||
* queue a new one for gapless playback */
|
||||
void (*about_to_finish) (GstPlayBin * playbin);
|
||||
|
||||
/* notify app that number of audio/video/text streams changed */
|
||||
void (*video_changed) (GstPlayBin * playbin);
|
||||
void (*audio_changed) (GstPlayBin * playbin);
|
||||
void (*text_changed) (GstPlayBin * playbin);
|
||||
|
||||
/* get audio/video/text tags for a stream */
|
||||
GstTagList *(*get_video_tags) (GstPlayBin * playbin, gint stream);
|
||||
GstTagList *(*get_audio_tags) (GstPlayBin * playbin, gint stream);
|
||||
GstTagList *(*get_text_tags) (GstPlayBin * playbin, gint stream);
|
||||
|
||||
/* get the last video frame and convert it to the given caps */
|
||||
GstBuffer *(*convert_frame) (GstPlayBin * playbin, GstCaps * caps);
|
||||
};
|
||||
|
||||
/* props */
|
||||
|
@ -429,6 +437,9 @@ static GstStructure *gst_play_bin_get_audio_tags (GstPlayBin * playbin,
|
|||
static GstStructure *gst_play_bin_get_text_tags (GstPlayBin * playbin,
|
||||
gint stream);
|
||||
|
||||
static GstBuffer *gst_play_bin_convert_frame (GstPlayBin * playbin,
|
||||
GstCaps * caps);
|
||||
|
||||
static gboolean setup_next_source (GstPlayBin * playbin);
|
||||
|
||||
static GstElementClass *parent_class;
|
||||
|
@ -441,6 +452,38 @@ GST_ELEMENT_DETAILS ("Player Bin 2",
|
|||
"Autoplug and play media from an uri",
|
||||
"Wim Taymans <wim.taymans@gmail.com>");
|
||||
|
||||
static void
|
||||
gst_play_marshal_BUFFER__BOXED (GClosure * closure,
|
||||
GValue * return_value G_GNUC_UNUSED,
|
||||
guint n_param_values,
|
||||
const GValue * param_values,
|
||||
gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data)
|
||||
{
|
||||
typedef GstBuffer *(*GMarshalFunc_OBJECT__BOXED) (gpointer data1,
|
||||
gpointer arg_1, gpointer data2);
|
||||
register GMarshalFunc_OBJECT__BOXED callback;
|
||||
register GCClosure *cc = (GCClosure *) closure;
|
||||
register gpointer data1, data2;
|
||||
GstBuffer *v_return;
|
||||
|
||||
g_return_if_fail (return_value != NULL);
|
||||
g_return_if_fail (n_param_values == 2);
|
||||
|
||||
if (G_CCLOSURE_SWAP_DATA (closure)) {
|
||||
data1 = closure->data;
|
||||
data2 = g_value_peek_pointer (param_values + 0);
|
||||
} else {
|
||||
data1 = g_value_peek_pointer (param_values + 0);
|
||||
data2 = closure->data;
|
||||
}
|
||||
callback =
|
||||
(GMarshalFunc_OBJECT__BOXED) (marshal_data ? marshal_data : cc->callback);
|
||||
|
||||
v_return = callback (data1, g_value_get_boxed (param_values + 1), data2);
|
||||
|
||||
gst_value_take_buffer (return_value, v_return);
|
||||
}
|
||||
|
||||
static GType
|
||||
gst_play_bin_get_type (void)
|
||||
{
|
||||
|
@ -509,6 +552,12 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
|
|||
g_param_spec_object ("source", "Source", "Source element",
|
||||
GST_TYPE_ELEMENT, G_PARAM_READABLE));
|
||||
|
||||
|
||||
/**
|
||||
* GstPlayBin:flags
|
||||
*
|
||||
* Control the behaviour of playbin.
|
||||
*/
|
||||
g_object_class_install_property (gobject_klass, PROP_FLAGS,
|
||||
g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
|
||||
GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS, G_PARAM_READWRITE));
|
||||
|
@ -596,6 +645,13 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
|
|||
"Mute the audio channel without changing the volume", FALSE,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
/**
|
||||
* GstPlayBin::frame
|
||||
* @playbin: a #GstPlayBin
|
||||
*
|
||||
* Get the currently rendered or prerolled frame in the sink.
|
||||
* The #GstCaps on the buffer will describe the format of the buffer.
|
||||
*/
|
||||
g_object_class_install_property (gobject_klass, PROP_FRAME,
|
||||
gst_param_spec_mini_object ("frame", "Frame",
|
||||
"The last frame (NULL = no video available)",
|
||||
|
@ -711,11 +767,33 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
|
|||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET (GstPlayBinClass, get_text_tags), NULL, NULL,
|
||||
gst_play_marshal_BOXED__INT, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
|
||||
/**
|
||||
* GstPlayBin::convert-frame
|
||||
* @playbin: a #GstPlayBin
|
||||
* @caps: the target format of the frame
|
||||
*
|
||||
* Action signal to retrieve the currently playing video frame in the format
|
||||
* specified by @caps.
|
||||
* If @caps is %NULL, no conversion will be performed and this function is
|
||||
* equivalent to the #GstPlayBin::frame property.
|
||||
*
|
||||
* Returns: a #GstBuffer of the current video frame converted to #caps.
|
||||
* The caps on the buffer will describe the final layout of the buffer data.
|
||||
* %NULL is returned when no current buffer can be retrieved or when the
|
||||
* conversion failed.
|
||||
*/
|
||||
gst_play_bin_signals[SIGNAL_GET_TEXT_TAGS] =
|
||||
g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET (GstPlayBinClass, convert_frame), NULL, NULL,
|
||||
gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
|
||||
|
||||
klass->get_video_tags = gst_play_bin_get_video_tags;
|
||||
klass->get_audio_tags = gst_play_bin_get_audio_tags;
|
||||
klass->get_text_tags = gst_play_bin_get_text_tags;
|
||||
|
||||
klass->convert_frame = gst_play_bin_convert_frame;
|
||||
|
||||
gst_element_class_set_details (gstelement_klass, &gst_play_bin_details);
|
||||
|
||||
gstelement_klass->change_state =
|
||||
|
@ -903,6 +981,22 @@ gst_play_bin_get_text_tags (GstPlayBin * playbin, gint stream)
|
|||
return result;
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_play_bin_convert_frame (GstPlayBin * playbin, GstCaps * caps)
|
||||
{
|
||||
GstBuffer *result;
|
||||
|
||||
result = gst_play_sink_get_last_frame (playbin->playsink);
|
||||
if (result != NULL && caps != NULL) {
|
||||
GstBuffer *temp;
|
||||
|
||||
temp = gst_play_frame_conv_convert (result, caps);
|
||||
gst_buffer_unref (result);
|
||||
result = temp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_play_bin_set_current_video_stream (GstPlayBin * playbin, gint stream)
|
||||
{
|
||||
|
@ -1183,6 +1277,7 @@ gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
|
|||
g_value_set_boolean (value, gst_play_sink_get_mute (playbin->playsink));
|
||||
break;
|
||||
case PROP_FRAME:
|
||||
gst_value_take_buffer (value, gst_play_bin_convert_frame (playbin, NULL));
|
||||
break;
|
||||
case PROP_FONT_DESC:
|
||||
break;
|
||||
|
@ -1568,6 +1663,9 @@ activate_group (GstPlayBin * playbin, GstSourceGroup * group)
|
|||
if (!uridecodebin)
|
||||
goto no_decodebin;
|
||||
|
||||
/* configure connection speed */
|
||||
g_object_set (uridecodebin, "connection-speed", playbin->connection_speed,
|
||||
NULL);
|
||||
/* configure uri */
|
||||
g_object_set (uridecodebin, "uri", group->uri, NULL);
|
||||
|
||||
|
|
|
@ -232,7 +232,7 @@ gst_play_sink_class_init (GstPlaySinkClass * klass)
|
|||
0.0, VOLUME_MAX_DOUBLE, 1.0, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_klass, PROP_FRAME,
|
||||
gst_param_spec_mini_object ("frame", "Frame",
|
||||
"The last frame (NULL = no video available)",
|
||||
"The last video frame (NULL = no video available)",
|
||||
GST_TYPE_BUFFER, G_PARAM_READABLE));
|
||||
g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
|
||||
g_param_spec_string ("subtitle-font-desc",
|
||||
|
@ -1358,6 +1358,32 @@ gst_play_sink_get_flags (GstPlaySink * playsink)
|
|||
return res;
|
||||
}
|
||||
|
||||
GstBuffer *
|
||||
gst_play_sink_get_last_frame (GstPlaySink * playsink)
|
||||
{
|
||||
GstBuffer *result = NULL;
|
||||
GstPlayVideoChain *chain;
|
||||
|
||||
GST_PLAY_SINK_LOCK (playsink);
|
||||
GST_DEBUG_OBJECT (playsink, "taking last frame");
|
||||
/* get the video chain if we can */
|
||||
if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
|
||||
GST_DEBUG_OBJECT (playsink, "found video chain");
|
||||
/* see if the chain is active */
|
||||
if (chain->chain.activated && chain->sink) {
|
||||
GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
|
||||
/* get the frame property if we can */
|
||||
if (g_object_class_find_property (G_OBJECT_GET_CLASS (chain->sink),
|
||||
"last-buffer")) {
|
||||
GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
|
||||
g_object_get (chain->sink, "last-buffer", &result, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
GST_PLAY_SINK_UNLOCK (playsink);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
GstPad *
|
||||
gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
|
||||
|
|
|
@ -78,6 +78,8 @@ gboolean gst_play_sink_get_mute (GstPlaySink *playsink);
|
|||
gboolean gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags);
|
||||
GstPlayFlags gst_play_sink_get_flags (GstPlaySink * playsink);
|
||||
|
||||
GstBuffer * gst_play_sink_get_last_frame (GstPlaySink * playsink);
|
||||
|
||||
gboolean gst_play_sink_reconfigure (GstPlaySink * playsink);
|
||||
|
||||
G_END_DECLS
|
||||
|
|
202
gst/playback/gstscreenshot.c
Normal file
202
gst/playback/gstscreenshot.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
/* Small helper element for format conversion
|
||||
* (c) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
||||
*
|
||||
* 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 <gst/gst.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gstscreenshot.h"
|
||||
|
||||
static void
|
||||
feed_fakesrc (GstElement * src, GstBuffer * buf, GstPad * pad, gpointer data)
|
||||
{
|
||||
GstBuffer *in_buf = GST_BUFFER (data);
|
||||
|
||||
gst_buffer_set_caps (buf, GST_BUFFER_CAPS (in_buf));
|
||||
|
||||
memcpy (GST_BUFFER_DATA (buf), GST_BUFFER_DATA (in_buf),
|
||||
GST_BUFFER_SIZE (in_buf));
|
||||
|
||||
GST_BUFFER_SIZE (buf) = GST_BUFFER_SIZE (in_buf);
|
||||
|
||||
GST_DEBUG ("feeding buffer %p, size %u, caps %" GST_PTR_FORMAT,
|
||||
buf, GST_BUFFER_SIZE (buf), GST_BUFFER_CAPS (buf));
|
||||
}
|
||||
|
||||
static void
|
||||
save_result (GstElement * sink, GstBuffer * buf, GstPad * pad, gpointer data)
|
||||
{
|
||||
GstBuffer **p_buf = (GstBuffer **) data;
|
||||
|
||||
*p_buf = gst_buffer_ref (buf);
|
||||
|
||||
GST_DEBUG ("received converted buffer %p with caps %" GST_PTR_FORMAT,
|
||||
*p_buf, GST_BUFFER_CAPS (*p_buf));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
create_element (const gchar * factory_name, GstElement ** element,
|
||||
GError ** err)
|
||||
{
|
||||
*element = gst_element_factory_make (factory_name, NULL);
|
||||
if (*element)
|
||||
return TRUE;
|
||||
|
||||
if (err && *err == NULL) {
|
||||
*err = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN,
|
||||
"cannot create element '%s' - please check your GStreamer installation",
|
||||
factory_name);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* takes ownership of the input buffer */
|
||||
GstBuffer *
|
||||
gst_play_frame_conv_convert (GstBuffer * buf, GstCaps * to_caps)
|
||||
{
|
||||
GstElement *src, *csp, *filter1, *vscale, *filter2, *sink, *pipeline;
|
||||
GstMessage *msg;
|
||||
GstBuffer *result = NULL;
|
||||
GError *error = NULL;
|
||||
GstBus *bus;
|
||||
GstCaps *to_caps_no_par;
|
||||
|
||||
g_return_val_if_fail (GST_BUFFER_CAPS (buf) != NULL, NULL);
|
||||
|
||||
/* videoscale is here to correct for the pixel-aspect-ratio for us */
|
||||
GST_DEBUG ("creating elements");
|
||||
if (!create_element ("fakesrc", &src, &error) ||
|
||||
!create_element ("ffmpegcolorspace", &csp, &error) ||
|
||||
!create_element ("videoscale", &vscale, &error) ||
|
||||
!create_element ("capsfilter", &filter1, &error) ||
|
||||
!create_element ("capsfilter", &filter2, &error) ||
|
||||
!create_element ("fakesink", &sink, &error))
|
||||
goto no_elements;
|
||||
|
||||
pipeline = gst_pipeline_new ("screenshot-pipeline");
|
||||
if (pipeline == NULL)
|
||||
goto no_pipeline;
|
||||
|
||||
GST_DEBUG ("adding elements");
|
||||
gst_bin_add_many (GST_BIN (pipeline), src, csp, filter1, vscale, filter2,
|
||||
sink, NULL);
|
||||
|
||||
g_signal_connect (src, "handoff", G_CALLBACK (feed_fakesrc), buf);
|
||||
|
||||
/* set to 'fixed' sizetype */
|
||||
g_object_set (src, "sizemax", GST_BUFFER_SIZE (buf), "sizetype", 2,
|
||||
"num-buffers", 1, "signal-handoffs", TRUE, NULL);
|
||||
|
||||
/* adding this superfluous capsfilter makes linking cheaper */
|
||||
to_caps_no_par = gst_caps_copy (to_caps);
|
||||
gst_structure_remove_field (gst_caps_get_structure (to_caps_no_par, 0),
|
||||
"pixel-aspect-ratio");
|
||||
g_object_set (filter1, "caps", to_caps_no_par, NULL);
|
||||
gst_caps_unref (to_caps_no_par);
|
||||
|
||||
g_object_set (filter2, "caps", to_caps, NULL);
|
||||
|
||||
g_signal_connect (sink, "handoff", G_CALLBACK (save_result), &result);
|
||||
|
||||
g_object_set (sink, "preroll-queue-len", 1, "signal-handoffs", TRUE, NULL);
|
||||
|
||||
/* FIXME: linking is still way too expensive, profile this properly */
|
||||
GST_DEBUG ("linking src->csp");
|
||||
if (!gst_element_link_pads (src, "src", csp, "sink"))
|
||||
return NULL;
|
||||
|
||||
GST_DEBUG ("linking csp->filter1");
|
||||
if (!gst_element_link_pads (csp, "src", filter1, "sink"))
|
||||
return NULL;
|
||||
|
||||
GST_DEBUG ("linking filter1->vscale");
|
||||
if (!gst_element_link_pads (filter1, "src", vscale, "sink"))
|
||||
return NULL;
|
||||
|
||||
GST_DEBUG ("linking vscale->capsfilter");
|
||||
if (!gst_element_link_pads (vscale, "src", filter2, "sink"))
|
||||
return NULL;
|
||||
|
||||
GST_DEBUG ("linking capsfilter->sink");
|
||||
if (!gst_element_link_pads (filter2, "src", sink, "sink"))
|
||||
return NULL;
|
||||
|
||||
GST_DEBUG ("running conversion pipeline");
|
||||
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||
|
||||
bus = gst_element_get_bus (pipeline);
|
||||
msg =
|
||||
gst_bus_poll (bus, GST_MESSAGE_ERROR | GST_MESSAGE_EOS, 25 * GST_SECOND);
|
||||
|
||||
if (msg) {
|
||||
switch (GST_MESSAGE_TYPE (msg)) {
|
||||
case GST_MESSAGE_EOS:{
|
||||
if (result) {
|
||||
GST_DEBUG ("conversion successful: result = %p", result);
|
||||
} else {
|
||||
GST_WARNING ("EOS but no result frame?!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_ERROR:{
|
||||
gchar *dbg = NULL;
|
||||
|
||||
gst_message_parse_error (msg, &error, &dbg);
|
||||
if (error) {
|
||||
g_warning ("Could not take screenshot: %s", error->message);
|
||||
GST_DEBUG ("%s [debug: %s]", error->message, GST_STR_NULL (dbg));
|
||||
g_error_free (error);
|
||||
} else {
|
||||
g_warning ("Could not take screenshot (and NULL error!)");
|
||||
}
|
||||
g_free (dbg);
|
||||
result = NULL;
|
||||
break;
|
||||
}
|
||||
default:{
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
g_warning ("Could not take screenshot: %s", "timeout during conversion");
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||
gst_object_unref (pipeline);
|
||||
|
||||
return result;
|
||||
|
||||
/* ERRORS */
|
||||
no_elements:
|
||||
{
|
||||
g_warning ("Could not take screenshot: %s", error->message);
|
||||
g_error_free (error);
|
||||
return NULL;
|
||||
}
|
||||
no_pipeline:
|
||||
{
|
||||
g_warning ("Could not take screenshot: %s", "no pipeline (unknown error)");
|
||||
return NULL;
|
||||
}
|
||||
}
|
31
gst/playback/gstscreenshot.h
Normal file
31
gst/playback/gstscreenshot.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* Small helper element for format conversion
|
||||
* (c) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
||||
*
|
||||
* 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_PLAY_FRAME_CONV_H__
|
||||
#define __GST_PLAY_FRAME_CONV_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GstBuffer * gst_play_frame_conv_convert (GstBuffer *buf, GstCaps *to);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_PLAY_FRAME_CONV_H__ */
|
|
@ -1625,6 +1625,68 @@ volume_spinbutton_changed_cb (GtkSpinButton * button, GstPipeline * pipeline)
|
|||
g_object_set (pipeline, "volume", volume, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
shot_cb (GtkButton * button, gpointer data)
|
||||
{
|
||||
GstBuffer *buffer;
|
||||
GstCaps *caps;
|
||||
|
||||
/* convert to our desired format (RGB24) */
|
||||
caps = gst_caps_new_simple ("video/x-raw-rgb",
|
||||
"bpp", G_TYPE_INT, 24, "depth", G_TYPE_INT, 24,
|
||||
/* Note: we don't ask for a specific width/height here, so that
|
||||
* videoscale can adjust dimensions from a non-1/1 pixel aspect
|
||||
* ratio to a 1/1 pixel-aspect-ratio */
|
||||
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
|
||||
"endianness", G_TYPE_INT, G_BIG_ENDIAN,
|
||||
"red_mask", G_TYPE_INT, 0xff0000,
|
||||
"green_mask", G_TYPE_INT, 0x00ff00,
|
||||
"blue_mask", G_TYPE_INT, 0x0000ff, NULL);
|
||||
|
||||
g_signal_emit_by_name (pipeline, "convert-frame", caps, &buffer);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
if (buffer) {
|
||||
GstCaps *caps;
|
||||
GstStructure *s;
|
||||
gboolean res;
|
||||
gint width, height;
|
||||
GdkPixbuf *pixbuf;
|
||||
GError *error = NULL;
|
||||
|
||||
/* get the snapshot buffer format now. We set the caps on the appsink so
|
||||
* that it can only be an rgb buffer. The only thing we have not specified
|
||||
* on the caps is the height, which is dependant on the pixel-aspect-ratio
|
||||
* of the source material */
|
||||
caps = GST_BUFFER_CAPS (buffer);
|
||||
if (!caps) {
|
||||
g_warning ("could not get snapshot format\n");
|
||||
goto done;
|
||||
}
|
||||
s = gst_caps_get_structure (caps, 0);
|
||||
|
||||
/* we need to get the final caps on the buffer to get the size */
|
||||
res = gst_structure_get_int (s, "width", &width);
|
||||
res |= gst_structure_get_int (s, "height", &height);
|
||||
if (!res) {
|
||||
g_warning ("could not get snapshot dimension\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* create pixmap from buffer and save, gstreamer video buffers have a stride
|
||||
* that is rounded up to the nearest multiple of 4 */
|
||||
pixbuf = gdk_pixbuf_new_from_data (GST_BUFFER_DATA (buffer),
|
||||
GDK_COLORSPACE_RGB, FALSE, 8, width, height,
|
||||
GST_ROUND_UP_4 (width * 3), NULL, NULL);
|
||||
|
||||
/* save the pixbuf */
|
||||
gdk_pixbuf_save (pixbuf, "snapshot.png", "png", &error, NULL);
|
||||
|
||||
done:
|
||||
gst_buffer_unref (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
|
||||
{
|
||||
|
@ -1757,8 +1819,8 @@ print_usage (int argc, char **argv)
|
|||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
GtkWidget *window, *hbox, *vbox, *panel, *boxes, *flagtable;
|
||||
GtkWidget *play_button, *pause_button, *stop_button;
|
||||
GtkWidget *window, *hbox, *vbox, *panel, *boxes, *flagtable, *boxes2;
|
||||
GtkWidget *play_button, *pause_button, *stop_button, *shot_button;
|
||||
GtkWidget *accurate_checkbox, *key_checkbox, *loop_checkbox, *flush_checkbox;
|
||||
GtkWidget *scrub_checkbox, *play_scrub_checkbox, *rate_spinbutton;
|
||||
GtkWidget *rate_label;
|
||||
|
@ -1874,7 +1936,6 @@ main (int argc, char **argv)
|
|||
if (pipeline_type == 16) {
|
||||
/* the playbin2 panel controls for the video/audio/subtitle tracks */
|
||||
panel = gtk_hbox_new (FALSE, 0);
|
||||
boxes = gtk_hbox_new (FALSE, 0);
|
||||
video_combo = gtk_combo_box_new_text ();
|
||||
audio_combo = gtk_combo_box_new_text ();
|
||||
text_combo = gtk_combo_box_new_text ();
|
||||
|
@ -1890,12 +1951,14 @@ main (int argc, char **argv)
|
|||
G_CALLBACK (audio_combo_cb), pipeline);
|
||||
g_signal_connect (G_OBJECT (text_combo), "changed",
|
||||
G_CALLBACK (text_combo_cb), pipeline);
|
||||
/* playbin2 panel for flag checkboxes and volume/mute */
|
||||
boxes = gtk_hbox_new (FALSE, 0);
|
||||
vis_checkbox = gtk_check_button_new_with_label ("Vis");
|
||||
video_checkbox = gtk_check_button_new_with_label ("Video");
|
||||
audio_checkbox = gtk_check_button_new_with_label ("Audio");
|
||||
text_checkbox = gtk_check_button_new_with_label ("Text");
|
||||
mute_checkbox = gtk_check_button_new_with_label ("Mute");
|
||||
volume_spinbutton = gtk_spin_button_new_with_range (0, 5.0, 0.1);
|
||||
volume_spinbutton = gtk_spin_button_new_with_range (0, 10.0, 0.1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON (volume_spinbutton), 1.0);
|
||||
gtk_box_pack_start (GTK_BOX (boxes), vis_checkbox, TRUE, TRUE, 2);
|
||||
gtk_box_pack_start (GTK_BOX (boxes), audio_checkbox, TRUE, TRUE, 2);
|
||||
|
@ -1920,8 +1983,17 @@ main (int argc, char **argv)
|
|||
G_CALLBACK (mute_toggle_cb), pipeline);
|
||||
g_signal_connect (G_OBJECT (volume_spinbutton), "value_changed",
|
||||
G_CALLBACK (volume_spinbutton_changed_cb), pipeline);
|
||||
/* playbin2 panel for snapshot */
|
||||
boxes2 = gtk_hbox_new (FALSE, 0);
|
||||
shot_button = gtk_button_new_from_stock (GTK_STOCK_SAVE);
|
||||
gtk_tooltips_set_tip (tips, shot_button,
|
||||
"save a screenshot .png in the current directory", NULL);
|
||||
gtk_box_pack_start (GTK_BOX (boxes2), shot_button, TRUE, TRUE, 2);
|
||||
g_signal_connect (G_OBJECT (shot_button), "clicked", G_CALLBACK (shot_cb),
|
||||
pipeline);
|
||||
|
||||
} else {
|
||||
panel = boxes = NULL;
|
||||
panel = boxes = boxes2 = NULL;
|
||||
}
|
||||
|
||||
/* do the packing stuff ... */
|
||||
|
@ -1943,9 +2015,10 @@ main (int argc, char **argv)
|
|||
gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_label, 3, 4, 0, 1);
|
||||
gtk_table_attach_defaults (GTK_TABLE (flagtable), rate_spinbutton, 3, 4, 1,
|
||||
2);
|
||||
if (panel && boxes) {
|
||||
if (panel && boxes && boxes2) {
|
||||
gtk_box_pack_start (GTK_BOX (vbox), panel, TRUE, TRUE, 2);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), boxes, TRUE, TRUE, 2);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), boxes2, TRUE, TRUE, 2);
|
||||
}
|
||||
gtk_box_pack_start (GTK_BOX (vbox), hscale, TRUE, TRUE, 2);
|
||||
|
||||
|
|
Loading…
Reference in a new issue