From 091ba8f6aaaf4df9c58cb7f84dc60c3ac22f07f2 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 14 May 2010 10:30:18 +0200 Subject: [PATCH] [MOVED FROM BAD 007/134] vp8enc: Add support for invisible frames and the Ogg mapping --- ext/vp8/gstvp8enc.c | 197 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 164 insertions(+), 33 deletions(-) diff --git a/ext/vp8/gstvp8enc.c b/ext/vp8/gstvp8enc.c index ef74b22..62a688f 100644 --- a/ext/vp8/gstvp8enc.c +++ b/ext/vp8/gstvp8enc.c @@ -1,6 +1,7 @@ /* VP8 * Copyright (C) 2006 David Schleef * Copyright (C) 2010 Entropy Wave Inc + * Copyright (C) 2010 Sebastian Dröge * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -29,6 +30,7 @@ #include #include #include +#include #include #include @@ -74,7 +76,7 @@ struct _GstVP8Enc int resolution_id; int n_frames; - + int keyframe_distance; }; struct _GstVP8EncClass @@ -82,6 +84,12 @@ struct _GstVP8EncClass GstBaseVideoEncoderClass base_video_encoder_class; }; +typedef struct +{ + vpx_image_t *image; + GList *invisible; +} GstVP8EncCoderHook; + /* GstVP8Enc signals and args */ enum { @@ -368,6 +376,12 @@ gst_vp8_enc_get_caps (GstBaseVideoEncoder * base_video_encoder) GstCaps *caps; const GstVideoState *state; GstVP8Enc *encoder; + GstTagList *tags; + GstBuffer *stream_hdr, *vorbiscomment; + guint8 *data; + GstStructure *s; + GValue array = { 0 }; + GValue value = { 0 }; encoder = GST_VP8_ENC (base_video_encoder); @@ -381,6 +395,50 @@ gst_vp8_enc_get_caps (GstBaseVideoEncoder * base_video_encoder) "pixel-aspect-ratio", GST_TYPE_FRACTION, state->par_n, state->par_d, NULL); + + /* Create Ogg stream-info */ + stream_hdr = gst_buffer_new_and_alloc (24); + data = GST_BUFFER_DATA (stream_hdr); + + GST_WRITE_UINT32_BE (data, 0x56503830); /* "VP80" */ + GST_WRITE_UINT8 (data + 4, 1); /* Major version 1 */ + GST_WRITE_UINT8 (data + 5, 0); /* Minor version 0 */ + GST_WRITE_UINT16_BE (data + 6, state->width); + GST_WRITE_UINT16_BE (data + 8, state->height); + GST_WRITE_UINT24_BE (data + 10, state->par_n); + GST_WRITE_UINT24_BE (data + 13, state->par_d); + GST_WRITE_UINT32_BE (data + 16, state->fps_n); + GST_WRITE_UINT32_BE (data + 20, state->fps_d); + + /* FIXME: Collect tags */ + tags = gst_tag_list_new (); + vorbiscomment = + gst_tag_list_to_vorbiscomment_buffer (tags, + (const guint8 *) "VP8_TAG", 7, NULL); + gst_tag_list_free (tags); + + /* mark buffers */ + GST_BUFFER_FLAG_SET (stream_hdr, GST_BUFFER_FLAG_IN_CAPS); + GST_BUFFER_FLAG_SET (vorbiscomment, GST_BUFFER_FLAG_IN_CAPS); + + s = gst_caps_get_structure (caps, 0); + + /* put buffers in a fixed list */ + g_value_init (&array, GST_TYPE_ARRAY); + g_value_init (&value, GST_TYPE_BUFFER); + gst_value_set_buffer (&value, stream_hdr); + gst_value_array_append_value (&array, &value); + g_value_unset (&value); + g_value_init (&value, GST_TYPE_BUFFER); + gst_value_set_buffer (&value, vorbiscomment); + gst_value_array_append_value (&array, &value); + gst_structure_set_value (s, "streamheader", &array); + g_value_unset (&value); + g_value_unset (&array); + + gst_buffer_unref (stream_hdr); + gst_buffer_unref (vorbiscomment); + return caps; } @@ -407,6 +465,8 @@ gst_vp8_enc_finish (GstBaseVideoEncoder * base_video_encoder) pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter); while (pkt != NULL) { + GstBuffer *buffer; + GstVP8EncCoderHook *hook; gboolean invisible, keyframe; GST_DEBUG ("packet %d type %d", pkt->data.frame.sz, pkt->kind); @@ -418,22 +478,26 @@ gst_vp8_enc_finish (GstBaseVideoEncoder * base_video_encoder) invisible = (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) != 0; keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0; - /* FIXME: This is wrong for invisible frames, we need to get - * a new frame that is not in the encoder list */ frame = gst_base_video_encoder_get_oldest_frame (base_video_encoder); - + hook = frame->coder_hook; /* FIXME: If frame is NULL something went really wrong! */ - frame->src_buffer = gst_buffer_new_and_alloc (pkt->data.frame.sz); + buffer = gst_buffer_new_and_alloc (pkt->data.frame.sz); - memcpy (GST_BUFFER_DATA (frame->src_buffer), - pkt->data.frame.buf, pkt->data.frame.sz); - frame->is_sync_point = keyframe; + memcpy (GST_BUFFER_DATA (buffer), pkt->data.frame.buf, pkt->data.frame.sz); + frame->is_sync_point = frame->is_sync_point || keyframe; - if (frame->coder_hook) - g_free (frame->coder_hook); + if (hook->image) + g_free (hook->image); + hook->image = NULL; - gst_base_video_encoder_finish_frame (base_video_encoder, frame); + if (invisible) { + hook->invisible = g_list_append (hook->invisible, buffer); + } else { + frame->src_buffer = buffer; + gst_base_video_encoder_finish_frame (base_video_encoder, frame); + frame = NULL; + } pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter); } @@ -486,6 +550,7 @@ gst_vp8_enc_handle_frame (GstBaseVideoEncoder * base_video_encoder, vpx_codec_iter_t iter = NULL; const vpx_codec_cx_pkt_t *pkt; vpx_image_t *image; + GstVP8EncCoderHook *hook; GST_DEBUG ("handle_frame"); @@ -543,7 +608,10 @@ gst_vp8_enc_handle_frame (GstBaseVideoEncoder * base_video_encoder, base_video_encoder->state.width, base_video_encoder->state.height, 1, GST_BUFFER_DATA (frame->sink_buffer)); - frame->coder_hook = image; + + hook = g_new0 (GstVP8EncCoderHook, 1); + hook->image = image; + frame->coder_hook = hook; #if 0 if (encoder->force_keyframe) { @@ -559,6 +627,7 @@ gst_vp8_enc_handle_frame (GstBaseVideoEncoder * base_video_encoder, pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter); while (pkt != NULL) { + GstBuffer *buffer; gboolean invisible, keyframe; GST_DEBUG ("packet %d type %d", pkt->data.frame.sz, pkt->kind); @@ -570,21 +639,25 @@ gst_vp8_enc_handle_frame (GstBaseVideoEncoder * base_video_encoder, invisible = (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) != 0; keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0; - /* FIXME: This is wrong for invisible frames, we need to get - * a new frame that is not in the encoder list */ frame = gst_base_video_encoder_get_oldest_frame (base_video_encoder); + hook = frame->coder_hook; /* FIXME: If frame is NULL something went really wrong! */ - frame->src_buffer = gst_buffer_new_and_alloc (pkt->data.frame.sz); + buffer = gst_buffer_new_and_alloc (pkt->data.frame.sz); - memcpy (GST_BUFFER_DATA (frame->src_buffer), - pkt->data.frame.buf, pkt->data.frame.sz); - frame->is_sync_point = keyframe; + memcpy (GST_BUFFER_DATA (buffer), pkt->data.frame.buf, pkt->data.frame.sz); + frame->is_sync_point = frame->is_sync_point || keyframe; - if (frame->coder_hook) - g_free (frame->coder_hook); + if (hook->image) + g_free (hook->image); + hook->image = NULL; - gst_base_video_encoder_finish_frame (base_video_encoder, frame); + if (invisible) { + hook->invisible = g_list_append (hook->invisible, buffer); + } else { + frame->src_buffer = buffer; + gst_base_video_encoder_finish_frame (base_video_encoder, frame); + } pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter); } @@ -592,40 +665,98 @@ gst_vp8_enc_handle_frame (GstBaseVideoEncoder * base_video_encoder, return TRUE; } +static guint64 +_to_granulepos (guint64 frame_end_number, guint inv_count, guint keyframe_dist) +{ + guint64 granulepos; + guint inv; + + inv = (inv_count == 0) ? 0x3 : inv_count - 1; + + granulepos = (frame_end_number << 32) | (inv << 30) | (keyframe_dist << 3); + return granulepos; +} + static GstFlowReturn gst_vp8_enc_shape_output (GstBaseVideoEncoder * base_video_encoder, GstVideoFrame * frame) { - GstBuffer *buf = frame->src_buffer; + GstVP8Enc *encoder; + GstBuffer *buf; const GstVideoState *state; GstFlowReturn ret; + GstVP8EncCoderHook *hook = frame->coder_hook; + GList *l; + gint inv_count; GST_DEBUG ("shape_output"); + encoder = GST_VP8_ENC (base_video_encoder); + state = gst_base_video_encoder_get_state (base_video_encoder); - GST_BUFFER_TIMESTAMP (buf) = gst_video_state_get_timestamp (state, - &base_video_encoder->segment, frame->presentation_frame_number); - GST_BUFFER_DURATION (buf) = gst_video_state_get_timestamp (state, - &base_video_encoder->segment, - frame->presentation_frame_number + 1) - GST_BUFFER_TIMESTAMP (buf); - GST_BUFFER_OFFSET (buf) = frame->presentation_frame_number; - GST_BUFFER_OFFSET_END (buf) = frame->presentation_frame_number + 1; + for (inv_count = 0, l = hook->invisible; l; inv_count++, l = l->next) { + buf = l->data; + + if (l == hook->invisible && frame->is_sync_point) { + GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + encoder->keyframe_distance = 0; + } else { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + encoder->keyframe_distance++; + } + + GST_BUFFER_TIMESTAMP (buf) = gst_video_state_get_timestamp (state, + &base_video_encoder->segment, frame->presentation_frame_number); + GST_BUFFER_DURATION (buf) = 0; + GST_BUFFER_OFFSET_END (buf) = + _to_granulepos (frame->presentation_frame_number + 1, + inv_count, encoder->keyframe_distance); + GST_BUFFER_OFFSET (buf) = + gst_util_uint64_scale (frame->presentation_frame_number + 1, + GST_SECOND * state->fps_d, state->fps_n); + + gst_buffer_set_caps (buf, base_video_encoder->caps); + ret = gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), buf); + + if (ret != GST_FLOW_OK) + goto done; + } - if (frame->is_sync_point) { + buf = frame->src_buffer; + frame->src_buffer = NULL; + + if (!hook->invisible && frame->is_sync_point) { GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + encoder->keyframe_distance = 0; } else { GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); + encoder->keyframe_distance++; } + GST_BUFFER_TIMESTAMP (buf) = gst_video_state_get_timestamp (state, + &base_video_encoder->segment, frame->presentation_frame_number); + GST_BUFFER_DURATION (buf) = gst_video_state_get_timestamp (state, + &base_video_encoder->segment, + frame->presentation_frame_number + 1) - GST_BUFFER_TIMESTAMP (buf); + GST_BUFFER_OFFSET_END (buf) = + _to_granulepos (frame->presentation_frame_number + 1, + 0, encoder->keyframe_distance); + GST_BUFFER_OFFSET (buf) = + gst_util_uint64_scale (frame->presentation_frame_number + 1, + GST_SECOND * state->fps_d, state->fps_n); + gst_buffer_set_caps (buf, base_video_encoder->caps); ret = gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), buf); - if (ret != GST_FLOW_OK) { + if (ret != GST_FLOW_OK) GST_ERROR ("flow error %d", ret); - } - frame->src_buffer = NULL; +done: + g_list_foreach (hook->invisible, (GFunc) gst_mini_object_unref, NULL); + g_list_free (hook->invisible); + g_free (hook); + frame->coder_hook = NULL; return ret; } -- 2.7.4