mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-16 05:16:36 +00:00
parent
5318e71b4d
commit
c95d0034af
3 changed files with 263 additions and 2 deletions
|
@ -9,10 +9,11 @@ libgsttheora_la_SOURCES = gsttheora.c \
|
|||
gsttheoradec.c \
|
||||
gsttheoraparse.c
|
||||
|
||||
libgsttheora_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(THEORA_CFLAGS)
|
||||
libgsttheora_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(THEORA_CFLAGS)
|
||||
libgsttheora_la_LIBADD = \
|
||||
$(top_builddir)/gst-libs/gst/tag/libgsttag-$(GST_MAJORMINOR).la \
|
||||
$(top_builddir)/gst-libs/gst/video/libgstvideo-$(GST_MAJORMINOR).la \
|
||||
$(GST_BASE_LIBS) \
|
||||
$(GST_LIBS) \
|
||||
$(THEORA_LIBS)
|
||||
libgsttheora_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
|
|
|
@ -85,6 +85,25 @@ gst_border_mode_get_type (void)
|
|||
return border_mode_type;
|
||||
}
|
||||
|
||||
#define GST_TYPE_MULTIPASS_MODE (gst_multipass_mode_get_type())
|
||||
static GType
|
||||
gst_multipass_mode_get_type (void)
|
||||
{
|
||||
static GType multipass_mode_type = 0;
|
||||
static const GEnumValue multipass_mode[] = {
|
||||
{MULTIPASS_MODE_SINGLE_PASS, "Single pass", "single-pass"},
|
||||
{MULTIPASS_MODE_FIRST_PASS, "First pass", "first-pass"},
|
||||
{MULTIPASS_MODE_SECOND_PASS, "Second pass", "second-pass"},
|
||||
{0, NULL, NULL},
|
||||
};
|
||||
|
||||
if (!multipass_mode_type) {
|
||||
multipass_mode_type =
|
||||
g_enum_register_static ("GstTheoraEncMultipassMode", multipass_mode);
|
||||
}
|
||||
return multipass_mode_type;
|
||||
}
|
||||
|
||||
/* taken from theora/lib/toplevel.c */
|
||||
static int
|
||||
_ilog (unsigned int v)
|
||||
|
@ -109,6 +128,8 @@ _ilog (unsigned int v)
|
|||
#define THEORA_DEF_CAP_OVERFLOW TRUE
|
||||
#define THEORA_DEF_CAP_UNDERFLOW FALSE
|
||||
#define THEORA_DEF_RATE_BUFFER 0
|
||||
#define THEORA_DEF_MULTIPASS_CACHE_FILE NULL
|
||||
#define THEORA_DEF_MULTIPASS_MODE MULTIPASS_MODE_SINGLE_PASS
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
@ -130,7 +151,9 @@ enum
|
|||
PROP_CAP_OVERFLOW,
|
||||
PROP_CAP_UNDERFLOW,
|
||||
PROP_RATE_BUFFER,
|
||||
/* FILL ME */
|
||||
PROP_MULTIPASS_CACHE_FILE,
|
||||
PROP_MULTIPASS_MODE
|
||||
/* FILL ME */
|
||||
};
|
||||
|
||||
/* this function does a straight granulepos -> timestamp conversion */
|
||||
|
@ -312,6 +335,15 @@ gst_theora_enc_class_init (GstTheoraEncClass * klass)
|
|||
" This property requires libtheora version >= 1.1",
|
||||
0, 1000, THEORA_DEF_RATE_BUFFER,
|
||||
(GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class, PROP_MULTIPASS_CACHE_FILE,
|
||||
g_param_spec_string ("multipass-cache-file", "Multipass Cache File",
|
||||
"Multipass cache file", THEORA_DEF_MULTIPASS_CACHE_FILE,
|
||||
(GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class, PROP_MULTIPASS_MODE,
|
||||
g_param_spec_enum ("multipass-mode", "Multipass mode",
|
||||
"Single pass or first/second pass", GST_TYPE_MULTIPASS_MODE,
|
||||
THEORA_DEF_MULTIPASS_MODE,
|
||||
(GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gstelement_class->change_state = theora_enc_change_state;
|
||||
GST_DEBUG_CATEGORY_INIT (theoraenc_debug, "theoraenc", 0, "Theora encoder");
|
||||
|
@ -349,6 +381,24 @@ gst_theora_enc_init (GstTheoraEnc * enc, GstTheoraEncClass * g_class)
|
|||
enc->cap_overflow = THEORA_DEF_CAP_OVERFLOW;
|
||||
enc->cap_underflow = THEORA_DEF_CAP_UNDERFLOW;
|
||||
enc->rate_buffer = THEORA_DEF_RATE_BUFFER;
|
||||
|
||||
enc->multipass_mode = THEORA_DEF_MULTIPASS_MODE;
|
||||
enc->multipass_cache_file = THEORA_DEF_MULTIPASS_CACHE_FILE;
|
||||
}
|
||||
|
||||
static void
|
||||
theora_enc_clear_multipass_cache (GstTheoraEnc * enc)
|
||||
{
|
||||
if (enc->multipass_cache_fd) {
|
||||
g_io_channel_shutdown (enc->multipass_cache_fd, TRUE, NULL);
|
||||
g_io_channel_unref (enc->multipass_cache_fd);
|
||||
enc->multipass_cache_fd = NULL;
|
||||
}
|
||||
|
||||
if (enc->multipass_cache_adapter) {
|
||||
gst_object_unref (enc->multipass_cache_adapter);
|
||||
enc->multipass_cache_adapter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -361,10 +411,51 @@ theora_enc_finalize (GObject * object)
|
|||
th_encode_free (enc->encoder);
|
||||
th_comment_clear (&enc->comment);
|
||||
th_info_clear (&enc->info);
|
||||
g_free (enc->multipass_cache_file);
|
||||
|
||||
theora_enc_clear_multipass_cache (enc);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
theora_enc_write_multipass_cache_beginning (GstTheoraEnc * enc, gboolean eos)
|
||||
{
|
||||
GError *err = NULL;
|
||||
GIOStatus stat;
|
||||
gint bytes_read = 0;
|
||||
gsize bytes_written = 0;
|
||||
gchar *buf;
|
||||
|
||||
stat =
|
||||
g_io_channel_seek_position (enc->multipass_cache_fd, 0, G_SEEK_SET, &err);
|
||||
if (stat != G_IO_STATUS_ERROR) {
|
||||
do {
|
||||
bytes_read =
|
||||
th_encode_ctl (enc->encoder, TH_ENCCTL_2PASS_OUT, &buf, sizeof (buf));
|
||||
if (bytes_read > 0)
|
||||
g_io_channel_write_chars (enc->multipass_cache_fd, buf, bytes_read,
|
||||
&bytes_written, NULL);
|
||||
} while (bytes_read > 0 && bytes_written > 0);
|
||||
|
||||
}
|
||||
|
||||
if (stat == G_IO_STATUS_ERROR || bytes_read < 0 || bytes_written < 0) {
|
||||
if (eos)
|
||||
GST_ELEMENT_WARNING (enc, RESOURCE, WRITE, (NULL),
|
||||
("Failed to seek to beginning of multipass cache file: %s",
|
||||
err->message));
|
||||
else
|
||||
GST_ELEMENT_ERROR (enc, RESOURCE, WRITE, (NULL),
|
||||
("Failed to seek to beginning of multipass cache file: %s",
|
||||
err->message));
|
||||
g_error_free (err);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
theora_enc_reset (GstTheoraEnc * enc)
|
||||
{
|
||||
|
@ -402,6 +493,11 @@ theora_enc_reset (GstTheoraEnc * enc)
|
|||
enc->keyframe_force : enc->keyframe_freq;
|
||||
th_encode_ctl (enc->encoder, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
|
||||
&keyframe_force, sizeof (keyframe_force));
|
||||
|
||||
/* Get placeholder data */
|
||||
if (enc->multipass_cache_fd
|
||||
&& enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS)
|
||||
theora_enc_write_multipass_cache_beginning (enc, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -736,6 +832,12 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event)
|
|||
enc->next_ts = next_time;
|
||||
}
|
||||
}
|
||||
if (enc->initialised && enc->multipass_cache_fd
|
||||
&& enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS)
|
||||
theora_enc_write_multipass_cache_beginning (enc, TRUE);
|
||||
|
||||
theora_enc_clear_multipass_cache (enc);
|
||||
|
||||
res = gst_pad_push_event (enc->srcpad, event);
|
||||
break;
|
||||
case GST_EVENT_FLUSH_STOP:
|
||||
|
@ -866,6 +968,79 @@ theora_enc_init_buffer (th_ycbcr_buffer buf, th_info * info, guint8 * data)
|
|||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
theora_enc_read_multipass_cache (GstTheoraEnc * enc)
|
||||
{
|
||||
GstBuffer *cache_buf;
|
||||
const guint8 *cache_data;
|
||||
gsize bytes_read = 0, bytes_consumed = 0;
|
||||
GIOStatus stat = G_IO_STATUS_NORMAL;
|
||||
gboolean done = FALSE;
|
||||
|
||||
while (!done) {
|
||||
if (gst_adapter_available (enc->multipass_cache_adapter) == 0) {
|
||||
cache_buf = gst_buffer_new_and_alloc (512);
|
||||
stat = g_io_channel_read_chars (enc->multipass_cache_fd,
|
||||
(gchar *) GST_BUFFER_DATA (cache_buf), GST_BUFFER_SIZE (cache_buf),
|
||||
&bytes_read, NULL);
|
||||
|
||||
if (bytes_read <= 0) {
|
||||
gst_buffer_unref (cache_buf);
|
||||
break;
|
||||
} else {
|
||||
GST_BUFFER_SIZE (cache_buf) = bytes_read;
|
||||
|
||||
gst_adapter_push (enc->multipass_cache_adapter, cache_buf);
|
||||
}
|
||||
}
|
||||
if (gst_adapter_available (enc->multipass_cache_adapter) == 0)
|
||||
break;
|
||||
|
||||
bytes_read =
|
||||
MIN (gst_adapter_available (enc->multipass_cache_adapter), 512);
|
||||
|
||||
cache_data = gst_adapter_peek (enc->multipass_cache_adapter, bytes_read);
|
||||
|
||||
bytes_consumed =
|
||||
th_encode_ctl (enc->encoder, TH_ENCCTL_2PASS_IN, (guint8 *) cache_data,
|
||||
bytes_read);
|
||||
done = bytes_consumed <= 0;
|
||||
if (bytes_consumed > 0)
|
||||
gst_adapter_flush (enc->multipass_cache_adapter, bytes_consumed);
|
||||
}
|
||||
|
||||
if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_EOF && bytes_read == 0)
|
||||
|| bytes_consumed < 0) {
|
||||
GST_ELEMENT_ERROR (enc, RESOURCE, READ, (NULL),
|
||||
("Failed to read multipass cache file"));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
theora_enc_write_multipass_cache (GstTheoraEnc * enc)
|
||||
{
|
||||
gchar *buf;
|
||||
gint bytes_read;
|
||||
gsize bytes_written = 0;
|
||||
|
||||
do {
|
||||
bytes_read =
|
||||
th_encode_ctl (enc->encoder, TH_ENCCTL_2PASS_OUT, &buf, sizeof (buf));
|
||||
if (bytes_read > 0)
|
||||
g_io_channel_write_chars (enc->multipass_cache_fd, buf, bytes_read,
|
||||
&bytes_written, NULL);
|
||||
} while (bytes_read > 0 && bytes_written > 0);
|
||||
|
||||
if (bytes_read < 0 || bytes_written < 0) {
|
||||
GST_ELEMENT_ERROR (enc, RESOURCE, WRITE, (NULL),
|
||||
("Failed to write multipass cache file"));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
theora_enc_chain (GstPad * pad, GstBuffer * buffer)
|
||||
{
|
||||
|
@ -1010,10 +1185,26 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
|
|||
enc->next_discont = TRUE;
|
||||
}
|
||||
|
||||
if (enc->multipass_cache_fd
|
||||
&& enc->multipass_mode == MULTIPASS_MODE_SECOND_PASS) {
|
||||
if (!theora_enc_read_multipass_cache (enc)) {
|
||||
ret = GST_FLOW_ERROR;
|
||||
goto multipass_read_failed;
|
||||
}
|
||||
}
|
||||
|
||||
res = th_encode_ycbcr_in (enc->encoder, ycbcr);
|
||||
/* none of the failure cases can happen here */
|
||||
g_assert (res == 0);
|
||||
|
||||
if (enc->multipass_cache_fd
|
||||
&& enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS) {
|
||||
if (!theora_enc_write_multipass_cache (enc)) {
|
||||
ret = GST_FLOW_ERROR;
|
||||
goto multipass_write_failed;
|
||||
}
|
||||
}
|
||||
|
||||
ret = GST_FLOW_OK;
|
||||
while (th_encode_packetout (enc->encoder, 0, &op)) {
|
||||
GstClockTime next_time;
|
||||
|
@ -1034,6 +1225,16 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
|
|||
return ret;
|
||||
|
||||
/* ERRORS */
|
||||
multipass_read_failed:
|
||||
{
|
||||
gst_buffer_unref (buffer);
|
||||
return ret;
|
||||
}
|
||||
multipass_write_failed:
|
||||
{
|
||||
gst_buffer_unref (buffer);
|
||||
return ret;
|
||||
}
|
||||
header_buffer_alloc:
|
||||
{
|
||||
gst_buffer_unref (buffer);
|
||||
|
@ -1075,6 +1276,33 @@ theora_enc_change_state (GstElement * element, GstStateChange transition)
|
|||
th_comment_init (&enc->comment);
|
||||
enc->packetno = 0;
|
||||
enc->force_keyframe = FALSE;
|
||||
|
||||
if (enc->multipass_mode >= MULTIPASS_MODE_FIRST_PASS) {
|
||||
GError *err = NULL;
|
||||
|
||||
if (!enc->multipass_cache_file) {
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
GST_ELEMENT_ERROR (enc, LIBRARY, SETTINGS, (NULL), (NULL));
|
||||
return ret;
|
||||
}
|
||||
enc->multipass_cache_fd =
|
||||
g_io_channel_new_file (enc->multipass_cache_file,
|
||||
(enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS ? "w" : "r"),
|
||||
&err);
|
||||
|
||||
if (enc->multipass_mode == MULTIPASS_MODE_SECOND_PASS)
|
||||
enc->multipass_cache_adapter = gst_adapter_new ();
|
||||
|
||||
if (!enc->multipass_cache_fd) {
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
GST_ELEMENT_ERROR (enc, RESOURCE, OPEN_READ, (NULL),
|
||||
("Failed to open multipass cache file: %s", err->message));
|
||||
g_error_free (err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
g_io_channel_set_encoding (enc->multipass_cache_fd, NULL, NULL);
|
||||
}
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||
break;
|
||||
|
@ -1159,6 +1387,12 @@ theora_enc_set_property (GObject * object, guint prop_id,
|
|||
case PROP_RATE_BUFFER:
|
||||
enc->rate_buffer = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_MULTIPASS_CACHE_FILE:
|
||||
enc->multipass_cache_file = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_MULTIPASS_MODE:
|
||||
enc->multipass_mode = g_value_get_enum (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -1226,6 +1460,12 @@ theora_enc_get_property (GObject * object, guint prop_id,
|
|||
case PROP_RATE_BUFFER:
|
||||
g_value_set_int (value, enc->rate_buffer);
|
||||
break;
|
||||
case PROP_MULTIPASS_CACHE_FILE:
|
||||
g_value_set_string (value, enc->multipass_cache_file);
|
||||
break;
|
||||
case PROP_MULTIPASS_MODE:
|
||||
g_value_set_enum (value, enc->multipass_mode);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define __GST_THEORAENC_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstadapter.h>
|
||||
#include <theora/theoraenc.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
@ -55,6 +56,20 @@ typedef enum
|
|||
}
|
||||
GstTheoraEncBorderMode;
|
||||
|
||||
/**
|
||||
* GstTheoraEncMultipassMode:
|
||||
* @MULTIPASS_MODE_SINGLE_PASS: Single pass encoding
|
||||
* @MULTIPASS_MODE_FIRST_PASS: First pass of two pass encoding
|
||||
* @MULTIPASS_MODE_SECOND_PASS: Second pass of two pass encoding
|
||||
*
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
MULTIPASS_MODE_SINGLE_PASS,
|
||||
MULTIPASS_MODE_FIRST_PASS,
|
||||
MULTIPASS_MODE_SECOND_PASS
|
||||
} GstTheoraEncMultipassMode;
|
||||
|
||||
/**
|
||||
* GstTheoraEnc:
|
||||
*
|
||||
|
@ -103,6 +118,11 @@ struct _GstTheoraEnc
|
|||
gboolean cap_overflow;
|
||||
gboolean cap_underflow;
|
||||
int rate_buffer;
|
||||
|
||||
GstTheoraEncMultipassMode multipass_mode;
|
||||
GIOChannel *multipass_cache_fd;
|
||||
GstAdapter *multipass_cache_adapter;
|
||||
gchar *multipass_cache_file;
|
||||
};
|
||||
|
||||
struct _GstTheoraEncClass
|
||||
|
|
Loading…
Reference in a new issue