GST_STATIC_CAPS ("video/x-h264")
);
-GST_BOILERPLATE (GstX264Enc, gst_x264_enc, GstElement, GST_TYPE_ELEMENT);
-
static void gst_x264_enc_finalize (GObject * object);
static void gst_x264_enc_reset (GstX264Enc * encoder);
static gboolean gst_x264_enc_init_encoder (GstX264Enc * encoder);
static void gst_x264_enc_close_encoder (GstX264Enc * encoder);
+static gboolean gst_x264_enc_sink_set_caps (GstPad * pad, GstCaps * caps);
static gboolean gst_x264_enc_sink_event (GstPad * pad, GstEvent * event);
static GstFlowReturn gst_x264_enc_chain (GstPad * pad, GstBuffer * buf);
static void gst_x264_enc_flush_frames (GstX264Enc * encoder, gboolean send);
static void gst_x264_enc_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
-
-/*
- * Returns: Buffer with the stream headers.
- */
-static GstBuffer *
-gst_x264_enc_header_buf (GstX264Enc * encoder)
-{
- GstBuffer *buf;
- x264_nal_t *nal;
- int i_nal;
- int header_return;
- int i_size;
- int nal_size, i_data;
- guint8 *buffer, *sps;
- gulong buffer_size;
-
- if (G_UNLIKELY (encoder->x264enc == NULL))
- return NULL;
-
- /* Create avcC header. */
-
- header_return = x264_encoder_headers (encoder->x264enc, &nal, &i_nal);
- if (header_return < 0) {
- GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Encode x264 header failed."),
- ("x264_encoder_headers return code=%d", header_return));
- return NULL;
- }
-
- /* This should be enough for a header buffer. */
- buffer_size = 100000;
- buffer = g_malloc (buffer_size);
-
- if (nal[1].i_type != 7 || nal[2].i_type != 8) {
- GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Unexpected x264 header."),
- ("TODO avcC header construction for high profiles needs some work"));
- return NULL;
- }
-
- sps = nal[1].p_payload;
-
- buffer[0] = 1; /* AVC Decoder Configuration Record ver. 1 */
- buffer[1] = sps[0]; /* profile_idc */
- buffer[2] = sps[1]; /* profile_compability */
- buffer[3] = sps[2]; /* level_idc */
- buffer[4] = 0xfc | (4 - 1); /* nal_length_size_minus1 */
-
- i_size = 5;
-
- buffer[i_size++] = 0xe0 | 1; /* number of SPSs */
-
- i_data = buffer_size - i_size - 2;
- nal_size = x264_nal_encode (buffer + i_size + 2, &i_data, 0, &nal[1]);
- GST_WRITE_UINT16_BE (buffer + i_size, nal_size);
- i_size += nal_size + 2;
-
- buffer[i_size++] = 1; /* number of PPSs */
-
- i_data = buffer_size - i_size - 2;
- nal_size = x264_nal_encode (buffer + i_size + 2, &i_data, 0, &nal[2]);
- GST_WRITE_UINT16_BE (buffer + i_size, nal_size);
- i_size += nal_size + 2;
-
- buf = gst_buffer_new_and_alloc (i_size);
-
- memcpy (GST_BUFFER_DATA (buf), buffer, i_size);
-
- g_free (buffer);
-
- return buf;
-}
-
-/* gst_x264_enc_set_src_caps
- * Returns: TRUE on success.
- */
-static gboolean
-gst_x264_enc_set_src_caps (GstX264Enc * encoder, GstPad * pad, GstCaps * caps)
-{
- GstStructure *structure;
- GstBuffer *buf;
- GstCaps *outcaps;
- gboolean res;
-
- structure = gst_caps_get_structure (caps, 0);
- structure = gst_structure_copy (structure);
- gst_structure_set_name (structure, "video/x-h264");
- outcaps = gst_caps_new_full (structure, NULL);
-
- if (!encoder->byte_stream) {
- buf = gst_x264_enc_header_buf (encoder);
- if (buf != NULL) {
- gst_caps_set_simple (outcaps, "codec_data", GST_TYPE_BUFFER, buf, NULL);
- gst_buffer_unref (buf);
- }
- }
-
- res = gst_pad_set_caps (pad, outcaps);
- gst_caps_unref (outcaps);
-
- return res;
-}
-
-static gboolean
-gst_x264_enc_sink_set_caps (GstPad * pad, GstCaps * caps)
-{
- GstX264Enc *encoder = GST_X264_ENC (GST_OBJECT_PARENT (pad));
- gint width, height;
- gint fps_num, fps_den;
- gint par_num, par_den;
- gint i;
-
- /* get info from caps */
- /* only I420 supported for now; so apparently claims x264enc ? */
- if (!gst_video_format_parse_caps (caps, &encoder->format, &width, &height) ||
- encoder->format != GST_VIDEO_FORMAT_I420)
- return FALSE;
- if (!gst_video_parse_caps_framerate (caps, &fps_num, &fps_den))
- return FALSE;
- if (!gst_video_parse_caps_pixel_aspect_ratio (caps, &par_num, &par_den)) {
- par_num = 1;
- par_den = 1;
- }
-
- /* If the encoder is initialized, do not
- reinitialize it again if not necessary */
- if (encoder->x264enc) {
- if (width == encoder->width && height == encoder->height
- && fps_num == encoder->fps_num && fps_den == encoder->fps_den
- && par_num == encoder->par_num && par_den == encoder->par_den)
- return TRUE;
-
- /* clear out pending frames */
- gst_x264_enc_flush_frames (encoder, TRUE);
-
- encoder->sps_id++;
- }
-
- /* store input description */
- encoder->width = width;
- encoder->height = height;
- encoder->fps_num = fps_num;
- encoder->fps_den = fps_den;
- encoder->par_num = par_num;
- encoder->par_den = par_den;
-
- /* prepare a cached image description */
- encoder->image_size = gst_video_format_get_size (encoder->format, width,
- height);
- for (i = 0; i < 3; ++i) {
- /* only offsets now, is shifted later */
- encoder->offset[i] = gst_video_format_get_component_offset (encoder->format,
- i, width, height);
- encoder->stride[i] = gst_video_format_get_row_stride (encoder->format,
- i, width);
- }
-
- if (!gst_x264_enc_init_encoder (encoder))
- return FALSE;
-
- if (!gst_x264_enc_set_src_caps (encoder, encoder->srcpad, caps)) {
- gst_x264_enc_close_encoder (encoder);
- return FALSE;
- }
-
- return TRUE;
-}
+GST_BOILERPLATE (GstX264Enc, gst_x264_enc, GstElement, GST_TYPE_ELEMENT);
static void
gst_x264_enc_base_init (gpointer g_class)
case X264_LOG_INFO:
gst_level = GST_LEVEL_INFO;
break;
- /* push x264enc debug down to our lower levels to avoid some clutter */
default:
+ /* push x264enc debug down to our lower levels to avoid some clutter */
gst_level = GST_LEVEL_LOG;
break;
}
encoder->last_timestamp = GST_CLOCK_TIME_NONE;
}
+static void
+gst_x264_enc_finalize (GObject * object)
+{
+ GstX264Enc *encoder = GST_X264_ENC (object);
+
+ g_free (encoder->stats_file);
+ encoder->stats_file = NULL;
+ g_free (encoder->buffer);
+ encoder->buffer = NULL;
+ g_queue_free (encoder->delay);
+ encoder->delay = NULL;
+
+ gst_x264_enc_close_encoder (encoder);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
/*
* gst_x264_enc_init_encoder
* @encoder: Encoder which should be initialized.
}
}
-static void
-gst_x264_enc_finalize (GObject * object)
+/*
+ * Returns: Buffer with the stream headers.
+ */
+static GstBuffer *
+gst_x264_enc_header_buf (GstX264Enc * encoder)
{
- GstX264Enc *encoder = GST_X264_ENC (object);
+ GstBuffer *buf;
+ x264_nal_t *nal;
+ int i_nal;
+ int header_return;
+ int i_size;
+ int nal_size, i_data;
+ guint8 *buffer, *sps;
+ gulong buffer_size;
- g_free (encoder->stats_file);
- encoder->stats_file = NULL;
- g_free (encoder->buffer);
- encoder->buffer = NULL;
- g_queue_free (encoder->delay);
- encoder->delay = NULL;
+ if (G_UNLIKELY (encoder->x264enc == NULL))
+ return NULL;
- gst_x264_enc_close_encoder (encoder);
+ /* Create avcC header. */
- G_OBJECT_CLASS (parent_class)->finalize (object);
+ header_return = x264_encoder_headers (encoder->x264enc, &nal, &i_nal);
+ if (header_return < 0) {
+ GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Encode x264 header failed."),
+ ("x264_encoder_headers return code=%d", header_return));
+ return NULL;
+ }
+
+ /* This should be enough for a header buffer. */
+ buffer_size = 100000;
+ buffer = g_malloc (buffer_size);
+
+ if (nal[1].i_type != 7 || nal[2].i_type != 8) {
+ GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Unexpected x264 header."),
+ ("TODO avcC header construction for high profiles needs some work"));
+ return NULL;
+ }
+
+ sps = nal[1].p_payload;
+
+ buffer[0] = 1; /* AVC Decoder Configuration Record ver. 1 */
+ buffer[1] = sps[0]; /* profile_idc */
+ buffer[2] = sps[1]; /* profile_compability */
+ buffer[3] = sps[2]; /* level_idc */
+ buffer[4] = 0xfc | (4 - 1); /* nal_length_size_minus1 */
+
+ i_size = 5;
+
+ buffer[i_size++] = 0xe0 | 1; /* number of SPSs */
+
+ i_data = buffer_size - i_size - 2;
+ nal_size = x264_nal_encode (buffer + i_size + 2, &i_data, 0, &nal[1]);
+ GST_WRITE_UINT16_BE (buffer + i_size, nal_size);
+ i_size += nal_size + 2;
+
+ buffer[i_size++] = 1; /* number of PPSs */
+
+ i_data = buffer_size - i_size - 2;
+ nal_size = x264_nal_encode (buffer + i_size + 2, &i_data, 0, &nal[2]);
+ GST_WRITE_UINT16_BE (buffer + i_size, nal_size);
+ i_size += nal_size + 2;
+
+ buf = gst_buffer_new_and_alloc (i_size);
+
+ memcpy (GST_BUFFER_DATA (buf), buffer, i_size);
+
+ g_free (buffer);
+
+ return buf;
}
-static void
-gst_x264_enc_flush_frames (GstX264Enc * encoder, gboolean send)
+/* gst_x264_enc_set_src_caps
+ * Returns: TRUE on success.
+ */
+static gboolean
+gst_x264_enc_set_src_caps (GstX264Enc * encoder, GstPad * pad, GstCaps * caps)
{
- GstFlowReturn flow_ret;
- gint i_nal;
+ GstStructure *structure;
+ GstBuffer *buf;
+ GstCaps *outcaps;
+ gboolean res;
- /* first send the remaining frames */
- if (encoder->x264enc)
- do {
- flow_ret = gst_x264_enc_encode_frame (encoder, NULL, &i_nal, send);
- } while (flow_ret == GST_FLOW_OK && i_nal > 0);
+ structure = gst_caps_get_structure (caps, 0);
+ structure = gst_structure_copy (structure);
+ gst_structure_set_name (structure, "video/x-h264");
+ outcaps = gst_caps_new_full (structure, NULL);
- /* in any case, make sure the delay queue is emptied */
- while (!g_queue_is_empty (encoder->delay))
- gst_buffer_unref (g_queue_pop_head (encoder->delay));
+ if (!encoder->byte_stream) {
+ buf = gst_x264_enc_header_buf (encoder);
+ if (buf != NULL) {
+ gst_caps_set_simple (outcaps, "codec_data", GST_TYPE_BUFFER, buf, NULL);
+ gst_buffer_unref (buf);
+ }
+ }
+
+ res = gst_pad_set_caps (pad, outcaps);
+ gst_caps_unref (outcaps);
+
+ return res;
+}
+
+static gboolean
+gst_x264_enc_sink_set_caps (GstPad * pad, GstCaps * caps)
+{
+ GstX264Enc *encoder = GST_X264_ENC (GST_OBJECT_PARENT (pad));
+ gint width, height;
+ gint fps_num, fps_den;
+ gint par_num, par_den;
+ gint i;
+
+ /* get info from caps */
+ /* only I420 supported for now; so apparently claims x264enc ? */
+ if (!gst_video_format_parse_caps (caps, &encoder->format, &width, &height) ||
+ encoder->format != GST_VIDEO_FORMAT_I420)
+ return FALSE;
+ if (!gst_video_parse_caps_framerate (caps, &fps_num, &fps_den))
+ return FALSE;
+ if (!gst_video_parse_caps_pixel_aspect_ratio (caps, &par_num, &par_den)) {
+ par_num = 1;
+ par_den = 1;
+ }
+
+ /* If the encoder is initialized, do not
+ reinitialize it again if not necessary */
+ if (encoder->x264enc) {
+ if (width == encoder->width && height == encoder->height
+ && fps_num == encoder->fps_num && fps_den == encoder->fps_den
+ && par_num == encoder->par_num && par_den == encoder->par_den)
+ return TRUE;
+
+ /* clear out pending frames */
+ gst_x264_enc_flush_frames (encoder, TRUE);
+
+ encoder->sps_id++;
+ }
+
+ /* store input description */
+ encoder->width = width;
+ encoder->height = height;
+ encoder->fps_num = fps_num;
+ encoder->fps_den = fps_den;
+ encoder->par_num = par_num;
+ encoder->par_den = par_den;
+
+ /* prepare a cached image description */
+ encoder->image_size = gst_video_format_get_size (encoder->format, width,
+ height);
+ for (i = 0; i < 3; ++i) {
+ /* only offsets now, is shifted later */
+ encoder->offset[i] = gst_video_format_get_component_offset (encoder->format,
+ i, width, height);
+ encoder->stride[i] = gst_video_format_get_row_stride (encoder->format,
+ i, width);
+ }
+
+ if (!gst_x264_enc_init_encoder (encoder))
+ return FALSE;
+
+ if (!gst_x264_enc_set_src_caps (encoder, encoder->srcpad, caps)) {
+ gst_x264_enc_close_encoder (encoder);
+ return FALSE;
+ }
+
+ return TRUE;
}
static gboolean
/* chain function
* this function does the actual processing
*/
-
static GstFlowReturn
gst_x264_enc_chain (GstPad * pad, GstBuffer * buf)
{
return gst_pad_push (encoder->srcpad, out_buf);
}
+static void
+gst_x264_enc_flush_frames (GstX264Enc * encoder, gboolean send)
+{
+ GstFlowReturn flow_ret;
+ gint i_nal;
+
+ /* first send the remaining frames */
+ if (encoder->x264enc)
+ do {
+ flow_ret = gst_x264_enc_encode_frame (encoder, NULL, &i_nal, send);
+ } while (flow_ret == GST_FLOW_OK && i_nal > 0);
+
+ /* in any case, make sure the delay queue is emptied */
+ while (!g_queue_is_empty (encoder->delay))
+ gst_buffer_unref (g_queue_pop_head (encoder->delay));
+}
+
static GstStateChangeReturn
gst_x264_enc_change_state (GstElement * element, GstStateChange transition)
{