static gboolean gst_base_video_encoder_sink_setcaps (GstPad * pad, GstCaps * caps) { GstBaseVideoEncoder *base_video_encoder; GstBaseVideoEncoderClass *base_video_encoder_class; gboolean ret; base_video_encoder = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); GST_DEBUG ("setcaps"); gst_base_video_state_from_caps (&base_video_encoder->state, caps); ret = base_video_encoder_class->set_format (base_video_encoder, &base_video_encoder->state); if (ret) { ret = base_video_encoder_class->start (base_video_encoder); } g_object_unref (base_video_encoder); return ret; }
static void gst_base_video_encoder_finalize (GObject * object) { GstBaseVideoEncoder *base_video_encoder; GstBaseVideoEncoderClass *base_video_encoder_class; GList *g; g_return_if_fail (GST_IS_BASE_VIDEO_ENCODER (object)); base_video_encoder = GST_BASE_VIDEO_ENCODER (object); base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (object); GST_DEBUG ("finalize"); for (g = base_video_encoder->frames; g; g = g_list_next (g)) { gst_base_video_codec_free_frame ((GstVideoFrame *) g->data); } g_list_free (base_video_encoder->frames); if (base_video_encoder->caps) { gst_caps_unref (base_video_encoder->caps); base_video_encoder->caps = NULL; } G_OBJECT_CLASS (parent_class)->finalize (object); }
static GstStateChangeReturn gst_base_video_encoder_change_state (GstElement * element, GstStateChange transition) { GstBaseVideoEncoder *base_video_encoder; GstBaseVideoEncoderClass *base_video_encoder_class; GstStateChangeReturn ret; base_video_encoder = GST_BASE_VIDEO_ENCODER (element); base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (element); switch (transition) { default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: if (base_video_encoder_class->stop) { base_video_encoder_class->stop (base_video_encoder); } break; default: break; } return ret; }
static gboolean gst_base_video_encoder_sink_setcaps (GstPad * pad, GstCaps * caps) { GstBaseVideoEncoder *base_video_encoder; GstBaseVideoEncoderClass *base_video_encoder_class; GstStructure *structure; GstVideoState *state; gboolean ret; base_video_encoder = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); GST_DEBUG ("setcaps"); state = &GST_BASE_VIDEO_CODEC (base_video_encoder)->state; structure = gst_caps_get_structure (caps, 0); gst_video_format_parse_caps (caps, &state->format, &state->width, &state->height); state->fps_n = 0; state->fps_d = 1; gst_video_parse_caps_framerate (caps, &state->fps_n, &state->fps_d); if (state->fps_d == 0) { state->fps_n = 0; state->fps_d = 1; } state->par_n = 1; state->par_d = 1; gst_video_parse_caps_pixel_aspect_ratio (caps, &state->par_n, &state->par_d); state->have_interlaced = gst_structure_get_boolean (structure, "interlaced", &state->interlaced); state->clean_width = state->width; state->clean_height = state->height; state->clean_offset_left = 0; state->clean_offset_top = 0; ret = base_video_encoder_class->set_format (base_video_encoder, &GST_BASE_VIDEO_CODEC (base_video_encoder)->state); if (ret) { ret = base_video_encoder_class->start (base_video_encoder); } g_object_unref (base_video_encoder); return ret; }
static GstFlowReturn gst_base_video_encoder_chain (GstPad * pad, GstBuffer * buf) { GstBaseVideoEncoder *base_video_encoder; GstBaseVideoEncoderClass *klass; GstVideoFrame *frame; if (!gst_pad_is_negotiated (pad)) { return GST_FLOW_NOT_NEGOTIATED; } base_video_encoder = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); klass = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); if (base_video_encoder->a.at_eos) { return GST_FLOW_UNEXPECTED; } if (base_video_encoder->sink_clipping) { gint64 start = GST_BUFFER_TIMESTAMP (buf); gint64 stop = start + GST_BUFFER_DURATION (buf); gint64 clip_start; gint64 clip_stop; if (!gst_segment_clip (&GST_BASE_VIDEO_CODEC (base_video_encoder)->segment, GST_FORMAT_TIME, start, stop, &clip_start, &clip_stop)) { GST_DEBUG ("clipping to segment dropped frame"); goto done; } } frame = gst_base_video_codec_new_frame (GST_BASE_VIDEO_CODEC (base_video_encoder)); frame->sink_buffer = buf; frame->presentation_timestamp = GST_BUFFER_TIMESTAMP (buf); frame->presentation_duration = GST_BUFFER_DURATION (buf); frame->presentation_frame_number = base_video_encoder->presentation_frame_number; base_video_encoder->presentation_frame_number++; GST_BASE_VIDEO_CODEC (base_video_encoder)->frames = g_list_append (GST_BASE_VIDEO_CODEC (base_video_encoder)->frames, frame); klass->handle_frame (base_video_encoder, frame); done: g_object_unref (base_video_encoder); return GST_FLOW_OK; }
static void gst_base_video_encoder_finalize (GObject * object) { GstBaseVideoEncoder *base_video_encoder; GstBaseVideoEncoderClass *base_video_encoder_class; g_return_if_fail (GST_IS_BASE_VIDEO_ENCODER (object)); base_video_encoder = GST_BASE_VIDEO_ENCODER (object); base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (object); GST_DEBUG ("finalize"); G_OBJECT_CLASS (parent_class)->finalize (object); }
static gboolean gst_base_video_encoder_sink_event (GstPad * pad, GstEvent * event) { GstBaseVideoEncoder *enc; GstBaseVideoEncoderClass *klass; gboolean handled = FALSE; gboolean ret = TRUE; enc = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); klass = GST_BASE_VIDEO_ENCODER_GET_CLASS (enc); GST_DEBUG_OBJECT (enc, "received event %d, %s", GST_EVENT_TYPE (event), GST_EVENT_TYPE_NAME (event)); if (klass->event) handled = klass->event (enc, event); if (!handled) handled = gst_base_video_encoder_sink_eventfunc (enc, event); if (!handled) { /* Forward non-serialized events and EOS/FLUSH_STOP immediately. * For EOS this is required because no buffer or serialized event * will come after EOS and nothing could trigger another * _finish_frame() call. * * If the subclass handles sending of EOS manually it can return * _DROPPED from ::finish() and all other subclasses should have * decoded/flushed all remaining data before this * * For FLUSH_STOP this is required because it is expected * to be forwarded immediately and no buffers are queued anyway. */ if (!GST_EVENT_IS_SERIALIZED (event) || GST_EVENT_TYPE (event) == GST_EVENT_EOS || GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) { ret = gst_pad_push_event (enc->base_video_codec.srcpad, event); } else { GST_BASE_VIDEO_CODEC_STREAM_LOCK (enc); enc->current_frame_events = g_list_prepend (enc->current_frame_events, event); GST_BASE_VIDEO_CODEC_STREAM_UNLOCK (enc); } } GST_DEBUG_OBJECT (enc, "event handled"); gst_object_unref (enc); return ret; }
static gboolean gst_base_video_encoder_src_event (GstPad * pad, GstEvent * event) { GstBaseVideoEncoder *base_video_encoder; GstBaseVideoEncoderClass *base_video_encoder_class; gboolean ret = FALSE; base_video_encoder = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CUSTOM_UPSTREAM: { const GstStructure *s; s = gst_event_get_structure (event); if (gst_structure_has_name (s, "GstForceKeyUnit")) { GST_OBJECT_LOCK (base_video_encoder); base_video_encoder->force_keyframe = TRUE; GST_OBJECT_UNLOCK (base_video_encoder); gst_event_unref (event); ret = TRUE; } else { ret = gst_pad_push_event (GST_BASE_VIDEO_CODEC_SINK_PAD (base_video_encoder), event); } break; } default: ret = gst_pad_push_event (GST_BASE_VIDEO_CODEC_SINK_PAD (base_video_encoder), event); break; } gst_object_unref (base_video_encoder); return ret; }
static gboolean gst_base_video_encoder_drain (GstBaseVideoEncoder * enc) { GstBaseVideoCodec *codec; GstBaseVideoEncoderClass *enc_class; gboolean ret = TRUE; codec = GST_BASE_VIDEO_CODEC (enc); enc_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (enc); GST_DEBUG_OBJECT (enc, "draining"); if (enc->drained) { GST_DEBUG_OBJECT (enc, "already drained"); return TRUE; } if (enc_class->reset) { GST_DEBUG_OBJECT (enc, "requesting subclass to finish"); ret = enc_class->reset (enc); } /* everything should be away now */ if (codec->frames) { /* not fatal/impossible though if subclass/codec eats stuff */ GST_WARNING_OBJECT (enc, "still %d frames left after draining", g_list_length (codec->frames)); #if 0 /* FIXME should do this, but subclass may come up with it later on ? * and would then need refcounting or so on frames */ g_list_foreach (codec->frames, (GFunc) gst_base_video_codec_free_frame, NULL); #endif } return ret; }
static gboolean gst_base_video_encoder_sink_event (GstPad * pad, GstEvent * event) { GstBaseVideoEncoder *base_video_encoder; GstBaseVideoEncoderClass *base_video_encoder_class; gboolean ret = FALSE; base_video_encoder = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: { if (base_video_encoder_class->finish) { base_video_encoder_class->finish (base_video_encoder); } ret = gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), event); } break; case GST_EVENT_NEWSEGMENT: { gboolean update; double rate; double applied_rate; GstFormat format; gint64 start; gint64 stop; gint64 position; gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, &format, &start, &stop, &position); if (format != GST_FORMAT_TIME) goto newseg_wrong_format; GST_DEBUG ("new segment %lld %lld", start, position); gst_segment_set_newsegment_full (&base_video_encoder->segment, update, rate, applied_rate, format, start, stop, position); ret = gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), event); } break; default: /* FIXME this changes the order of events */ ret = gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), event); break; } done: gst_object_unref (base_video_encoder); return ret; newseg_wrong_format: { GST_DEBUG_OBJECT (base_video_encoder, "received non TIME newsegment"); gst_event_unref (event); goto done; } }
GstFlowReturn gst_base_video_encoder_finish_frame (GstBaseVideoEncoder * base_video_encoder, GstVideoFrame * frame) { GstFlowReturn ret; GstBaseVideoEncoderClass *base_video_encoder_class; base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); frame->system_frame_number = GST_BASE_VIDEO_CODEC (base_video_encoder)->system_frame_number; GST_BASE_VIDEO_CODEC (base_video_encoder)->system_frame_number++; if (frame->is_sync_point) { base_video_encoder->distance_from_sync = 0; GST_BUFFER_FLAG_UNSET (frame->src_buffer, GST_BUFFER_FLAG_DELTA_UNIT); } else { GST_BUFFER_FLAG_SET (frame->src_buffer, GST_BUFFER_FLAG_DELTA_UNIT); } frame->distance_from_sync = base_video_encoder->distance_from_sync; base_video_encoder->distance_from_sync++; frame->decode_frame_number = frame->system_frame_number - 1; if (frame->decode_frame_number < 0) { frame->decode_timestamp = 0; } else { frame->decode_timestamp = gst_util_uint64_scale (frame->decode_frame_number, GST_SECOND * GST_BASE_VIDEO_CODEC (base_video_encoder)->state.fps_d, GST_BASE_VIDEO_CODEC (base_video_encoder)->state.fps_n); } GST_BUFFER_TIMESTAMP (frame->src_buffer) = frame->presentation_timestamp; GST_BUFFER_DURATION (frame->src_buffer) = frame->presentation_duration; GST_BUFFER_OFFSET (frame->src_buffer) = frame->decode_timestamp; GST_BASE_VIDEO_CODEC (base_video_encoder)->frames = g_list_remove (GST_BASE_VIDEO_CODEC (base_video_encoder)->frames, frame); if (!base_video_encoder->set_output_caps) { if (base_video_encoder_class->get_caps) { GST_BASE_VIDEO_CODEC (base_video_encoder)->caps = base_video_encoder_class->get_caps (base_video_encoder); } else { GST_BASE_VIDEO_CODEC (base_video_encoder)->caps = gst_caps_new_simple ("video/unknown", NULL); } gst_pad_set_caps (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), GST_BASE_VIDEO_CODEC (base_video_encoder)->caps); base_video_encoder->set_output_caps = TRUE; } gst_buffer_set_caps (GST_BUFFER (frame->src_buffer), GST_BASE_VIDEO_CODEC (base_video_encoder)->caps); if (frame->force_keyframe) { GstClockTime stream_time; GstClockTime running_time; GstStructure *s; running_time = gst_segment_to_running_time (&GST_BASE_VIDEO_CODEC (base_video_encoder)->segment, GST_FORMAT_TIME, frame->presentation_timestamp); stream_time = gst_segment_to_stream_time (&GST_BASE_VIDEO_CODEC (base_video_encoder)->segment, GST_FORMAT_TIME, frame->presentation_timestamp); /* FIXME this should send the event that we got on the sink pad instead of creating a new one */ s = gst_structure_new ("GstForceKeyUnit", "timestamp", G_TYPE_UINT64, frame->presentation_timestamp, "stream-time", G_TYPE_UINT64, stream_time, "running-time", G_TYPE_UINT64, running_time, NULL); gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s)); } if (base_video_encoder_class->shape_output) { ret = base_video_encoder_class->shape_output (base_video_encoder, frame); } else { ret = gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), frame->src_buffer); } gst_base_video_codec_free_frame (frame); return ret; }
static gboolean gst_base_video_encoder_sink_setcaps (GstPad * pad, GstCaps * caps) { GstBaseVideoEncoder *base_video_encoder; GstBaseVideoEncoderClass *base_video_encoder_class; GstStructure *structure; GstVideoState *state, tmp_state; gboolean ret; gboolean changed = FALSE; base_video_encoder = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); /* subclass should do something here ... */ g_return_val_if_fail (base_video_encoder_class->set_format != NULL, FALSE); GST_DEBUG_OBJECT (base_video_encoder, "setcaps %" GST_PTR_FORMAT, caps); GST_BASE_VIDEO_CODEC_STREAM_LOCK (base_video_encoder); state = &GST_BASE_VIDEO_CODEC (base_video_encoder)->state; memset (&tmp_state, 0, sizeof (tmp_state)); tmp_state.caps = gst_caps_ref (caps); structure = gst_caps_get_structure (caps, 0); ret = gst_video_format_parse_caps (caps, &tmp_state.format, &tmp_state.width, &tmp_state.height); if (!ret) goto exit; changed = (tmp_state.format != state->format || tmp_state.width != state->width || tmp_state.height != state->height); if (!gst_video_parse_caps_framerate (caps, &tmp_state.fps_n, &tmp_state.fps_d)) { tmp_state.fps_n = 0; tmp_state.fps_d = 1; } changed = changed || (tmp_state.fps_n != state->fps_n || tmp_state.fps_d != state->fps_d); if (!gst_video_parse_caps_pixel_aspect_ratio (caps, &tmp_state.par_n, &tmp_state.par_d)) { tmp_state.par_n = 1; tmp_state.par_d = 1; } changed = changed || (tmp_state.par_n != state->par_n || tmp_state.par_d != state->par_d); tmp_state.have_interlaced = gst_structure_get_boolean (structure, "interlaced", &tmp_state.interlaced); changed = changed || (tmp_state.have_interlaced != state->have_interlaced || tmp_state.interlaced != state->interlaced); tmp_state.bytes_per_picture = gst_video_format_get_size (tmp_state.format, tmp_state.width, tmp_state.height); tmp_state.clean_width = tmp_state.width; tmp_state.clean_height = tmp_state.height; tmp_state.clean_offset_left = 0; tmp_state.clean_offset_top = 0; if (changed) { /* arrange draining pending frames */ gst_base_video_encoder_drain (base_video_encoder); /* and subclass should be ready to configure format at any time around */ if (base_video_encoder_class->set_format) ret = base_video_encoder_class->set_format (base_video_encoder, &tmp_state); if (ret) { gst_caps_replace (&state->caps, NULL); *state = tmp_state; } } else { /* no need to stir things up */ GST_DEBUG_OBJECT (base_video_encoder, "new video format identical to configured format"); gst_caps_unref (tmp_state.caps); ret = TRUE; } exit: GST_BASE_VIDEO_CODEC_STREAM_UNLOCK (base_video_encoder); if (!ret) { GST_WARNING_OBJECT (base_video_encoder, "rejected caps %" GST_PTR_FORMAT, caps); } g_object_unref (base_video_encoder); return ret; }
static gboolean gst_base_video_encoder_sink_event (GstPad * pad, GstEvent * event) { GstBaseVideoEncoder *base_video_encoder; GstBaseVideoEncoderClass *base_video_encoder_class; gboolean ret = FALSE; base_video_encoder = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: { if (base_video_encoder_class->finish) { base_video_encoder_class->finish (base_video_encoder); } ret = gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), event); } break; case GST_EVENT_NEWSEGMENT: { gboolean update; double rate; double applied_rate; GstFormat format; gint64 start; gint64 stop; gint64 position; gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, &format, &start, &stop, &position); if (format != GST_FORMAT_TIME) goto newseg_wrong_format; GST_DEBUG ("new segment %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (position)); gst_segment_set_newsegment_full (&GST_BASE_VIDEO_CODEC (base_video_encoder)->segment, update, rate, applied_rate, format, start, stop, position); ret = gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), event); } break; case GST_EVENT_CUSTOM_DOWNSTREAM: { const GstStructure *s; s = gst_event_get_structure (event); if (gst_structure_has_name (s, "GstForceKeyUnit")) { GST_OBJECT_LOCK (base_video_encoder); base_video_encoder->force_keyframe = TRUE; GST_OBJECT_UNLOCK (base_video_encoder); gst_event_unref (event); ret = GST_FLOW_OK; } else { ret = gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), event); } break; } default: /* FIXME this changes the order of events */ ret = gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), event); break; } done: gst_object_unref (base_video_encoder); return ret; newseg_wrong_format: { GST_DEBUG_OBJECT (base_video_encoder, "received non TIME newsegment"); gst_event_unref (event); goto done; } }
static gboolean gst_base_video_encoder_sink_eventfunc (GstBaseVideoEncoder * base_video_encoder, GstEvent * event) { GstBaseVideoEncoderClass *base_video_encoder_class; gboolean ret = FALSE; base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: { GstFlowReturn flow_ret; GST_BASE_VIDEO_CODEC_STREAM_LOCK (base_video_encoder); base_video_encoder->a.at_eos = TRUE; if (base_video_encoder_class->finish) { flow_ret = base_video_encoder_class->finish (base_video_encoder); } else { flow_ret = GST_FLOW_OK; } ret = (flow_ret == GST_BASE_VIDEO_ENCODER_FLOW_DROPPED); GST_BASE_VIDEO_CODEC_STREAM_UNLOCK (base_video_encoder); break; } case GST_EVENT_NEWSEGMENT: { gboolean update; double rate; double applied_rate; GstFormat format; gint64 start; gint64 stop; gint64 position; GST_BASE_VIDEO_CODEC_STREAM_LOCK (base_video_encoder); gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, &format, &start, &stop, &position); GST_DEBUG_OBJECT (base_video_encoder, "newseg rate %g, applied rate %g, " "format %d, start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT ", pos = %" GST_TIME_FORMAT, rate, applied_rate, format, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (position)); if (format != GST_FORMAT_TIME) { GST_DEBUG_OBJECT (base_video_encoder, "received non TIME newsegment"); GST_BASE_VIDEO_CODEC_STREAM_UNLOCK (base_video_encoder); break; } base_video_encoder->a.at_eos = FALSE; gst_segment_set_newsegment_full (&GST_BASE_VIDEO_CODEC (base_video_encoder)->segment, update, rate, applied_rate, format, start, stop, position); GST_BASE_VIDEO_CODEC_STREAM_UNLOCK (base_video_encoder); break; } case GST_EVENT_CUSTOM_DOWNSTREAM: { const GstStructure *s; s = gst_event_get_structure (event); if (gst_structure_has_name (s, "GstForceKeyUnit")) { GST_OBJECT_LOCK (base_video_encoder); base_video_encoder->force_keyframe = TRUE; if (base_video_encoder->force_keyunit_event) gst_event_unref (base_video_encoder->force_keyunit_event); base_video_encoder->force_keyunit_event = gst_event_copy (event); GST_OBJECT_UNLOCK (base_video_encoder); gst_event_unref (event); ret = TRUE; } break; } default: break; } return ret; }
GstFlowReturn gst_base_video_encoder_finish_frame (GstBaseVideoEncoder * base_video_encoder, GstVideoFrame * frame) { GstFlowReturn ret; GstBaseVideoEncoderClass *base_video_encoder_class; base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); frame->system_frame_number = base_video_encoder->system_frame_number; base_video_encoder->system_frame_number++; if (frame->is_sync_point) { base_video_encoder->distance_from_sync = 0; GST_BUFFER_FLAG_UNSET (frame->src_buffer, GST_BUFFER_FLAG_DELTA_UNIT); } else { GST_BUFFER_FLAG_SET (frame->src_buffer, GST_BUFFER_FLAG_DELTA_UNIT); } frame->distance_from_sync = base_video_encoder->distance_from_sync; base_video_encoder->distance_from_sync++; frame->decode_frame_number = frame->system_frame_number - 1; if (frame->decode_frame_number < 0) { frame->decode_timestamp = 0; } else { frame->decode_timestamp = gst_util_uint64_scale (frame->decode_frame_number, GST_SECOND * base_video_encoder->state.fps_d, base_video_encoder->state.fps_n); } GST_BUFFER_TIMESTAMP (frame->src_buffer) = frame->presentation_timestamp; GST_BUFFER_DURATION (frame->src_buffer) = frame->presentation_duration; GST_BUFFER_OFFSET (frame->src_buffer) = frame->decode_timestamp; base_video_encoder->frames = g_list_remove (base_video_encoder->frames, frame); if (!base_video_encoder->set_output_caps) { if (base_video_encoder_class->get_caps) { base_video_encoder->caps = base_video_encoder_class->get_caps (base_video_encoder); } else { base_video_encoder->caps = gst_caps_new_simple ("video/unknown", NULL); } gst_pad_set_caps (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), base_video_encoder->caps); base_video_encoder->set_output_caps = TRUE; } gst_buffer_set_caps (GST_BUFFER (frame->src_buffer), base_video_encoder->caps); if (base_video_encoder_class->shape_output) { ret = base_video_encoder_class->shape_output (base_video_encoder, frame); } else { ret = gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), frame->src_buffer); } gst_base_video_codec_free_frame (frame); return ret; }
/** * gst_base_video_encoder_finish_frame: * @base_video_encoder: a #GstBaseVideoEncoder * @frame: an encoded #GstVideoFrame * * @frame must have a valid encoded data buffer, whose metadata fields * are then appropriately set according to frame data or no buffer at * all if the frame should be dropped. * It is subsequently pushed downstream or provided to @shape_output. * In any case, the frame is considered finished and released. * * Returns: a #GstFlowReturn resulting from sending data downstream */ GstFlowReturn gst_base_video_encoder_finish_frame (GstBaseVideoEncoder * base_video_encoder, GstVideoFrame * frame) { GstFlowReturn ret = GST_FLOW_OK; GstBaseVideoEncoderClass *base_video_encoder_class; GList *l; base_video_encoder_class = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); GST_LOG_OBJECT (base_video_encoder, "finish frame fpn %d", frame->presentation_frame_number); GST_BASE_VIDEO_CODEC_STREAM_LOCK (base_video_encoder); /* Push all pending events that arrived before this frame */ for (l = base_video_encoder->base_video_codec.frames; l; l = l->next) { GstVideoFrame *tmp = l->data; if (tmp->events) { GList *k; for (k = g_list_last (tmp->events); k; k = k->prev) gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), k->data); g_list_free (tmp->events); tmp->events = NULL; } if (tmp == frame) break; } if (frame->force_keyframe) { GstClockTime stream_time; GstClockTime running_time; GstEvent *ev; running_time = gst_segment_to_running_time (&GST_BASE_VIDEO_CODEC (base_video_encoder)->segment, GST_FORMAT_TIME, frame->presentation_timestamp); stream_time = gst_segment_to_stream_time (&GST_BASE_VIDEO_CODEC (base_video_encoder)->segment, GST_FORMAT_TIME, frame->presentation_timestamp); /* re-use upstream event if any so it also conveys any additional * info upstream arranged in there */ GST_OBJECT_LOCK (base_video_encoder); if (base_video_encoder->force_keyunit_event) { ev = base_video_encoder->force_keyunit_event; base_video_encoder->force_keyunit_event = NULL; } else { ev = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, gst_structure_new ("GstForceKeyUnit", NULL)); } GST_OBJECT_UNLOCK (base_video_encoder); gst_structure_set (ev->structure, "timestamp", G_TYPE_UINT64, frame->presentation_timestamp, "stream-time", G_TYPE_UINT64, stream_time, "running-time", G_TYPE_UINT64, running_time, NULL); gst_pad_push_event (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), ev); } /* no buffer data means this frame is skipped/dropped */ if (!frame->src_buffer) { GST_DEBUG_OBJECT (base_video_encoder, "skipping frame %" GST_TIME_FORMAT, GST_TIME_ARGS (frame->presentation_timestamp)); goto done; } if (frame->is_sync_point) { GST_LOG_OBJECT (base_video_encoder, "key frame"); base_video_encoder->distance_from_sync = 0; GST_BUFFER_FLAG_UNSET (frame->src_buffer, GST_BUFFER_FLAG_DELTA_UNIT); } else { GST_BUFFER_FLAG_SET (frame->src_buffer, GST_BUFFER_FLAG_DELTA_UNIT); } frame->distance_from_sync = base_video_encoder->distance_from_sync; base_video_encoder->distance_from_sync++; frame->decode_frame_number = frame->system_frame_number - 1; if (frame->decode_frame_number < 0) { frame->decode_timestamp = 0; } else { frame->decode_timestamp = gst_util_uint64_scale (frame->decode_frame_number, GST_SECOND * GST_BASE_VIDEO_CODEC (base_video_encoder)->state.fps_d, GST_BASE_VIDEO_CODEC (base_video_encoder)->state.fps_n); } GST_BUFFER_TIMESTAMP (frame->src_buffer) = frame->presentation_timestamp; GST_BUFFER_DURATION (frame->src_buffer) = frame->presentation_duration; GST_BUFFER_OFFSET (frame->src_buffer) = frame->decode_timestamp; /* update rate estimate */ GST_BASE_VIDEO_CODEC (base_video_encoder)->bytes += GST_BUFFER_SIZE (frame->src_buffer); if (GST_CLOCK_TIME_IS_VALID (frame->presentation_duration)) { GST_BASE_VIDEO_CODEC (base_video_encoder)->time += frame->presentation_duration; } else { /* better none than nothing valid */ GST_BASE_VIDEO_CODEC (base_video_encoder)->time = GST_CLOCK_TIME_NONE; } if (G_UNLIKELY (GST_BASE_VIDEO_CODEC (base_video_encoder)->discont)) { GST_LOG_OBJECT (base_video_encoder, "marking discont"); GST_BUFFER_FLAG_SET (frame->src_buffer, GST_BUFFER_FLAG_DISCONT); GST_BASE_VIDEO_CODEC (base_video_encoder)->discont = FALSE; } gst_buffer_set_caps (GST_BUFFER (frame->src_buffer), GST_PAD_CAPS (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder))); if (base_video_encoder_class->shape_output) { ret = base_video_encoder_class->shape_output (base_video_encoder, frame); } else { ret = gst_pad_push (GST_BASE_VIDEO_CODEC_SRC_PAD (base_video_encoder), frame->src_buffer); } frame->src_buffer = NULL; done: /* handed out */ GST_BASE_VIDEO_CODEC (base_video_encoder)->frames = g_list_remove (GST_BASE_VIDEO_CODEC (base_video_encoder)->frames, frame); gst_base_video_codec_free_frame (frame); GST_BASE_VIDEO_CODEC_STREAM_UNLOCK (base_video_encoder); return ret; }
static GstFlowReturn gst_base_video_encoder_chain (GstPad * pad, GstBuffer * buf) { GstBaseVideoEncoder *base_video_encoder; GstBaseVideoEncoderClass *klass; GstVideoFrame *frame; GstFlowReturn ret = GST_FLOW_OK; base_video_encoder = GST_BASE_VIDEO_ENCODER (gst_pad_get_parent (pad)); klass = GST_BASE_VIDEO_ENCODER_GET_CLASS (base_video_encoder); g_return_val_if_fail (klass->handle_frame != NULL, GST_FLOW_ERROR); GST_BASE_VIDEO_CODEC_STREAM_LOCK (base_video_encoder); if (!GST_PAD_CAPS (pad)) { ret = GST_FLOW_NOT_NEGOTIATED; goto done; } GST_LOG_OBJECT (base_video_encoder, "received buffer of size %d with ts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, GST_BUFFER_SIZE (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); if (base_video_encoder->a.at_eos) { ret = GST_FLOW_UNEXPECTED; goto done; } if (base_video_encoder->sink_clipping) { gint64 start = GST_BUFFER_TIMESTAMP (buf); gint64 stop = start + GST_BUFFER_DURATION (buf); gint64 clip_start; gint64 clip_stop; if (!gst_segment_clip (&GST_BASE_VIDEO_CODEC (base_video_encoder)->segment, GST_FORMAT_TIME, start, stop, &clip_start, &clip_stop)) { GST_DEBUG_OBJECT (base_video_encoder, "clipping to segment dropped frame"); goto done; } } if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))) { GST_LOG_OBJECT (base_video_encoder, "marked discont"); GST_BASE_VIDEO_CODEC (base_video_encoder)->discont = TRUE; } frame = gst_base_video_codec_new_frame (GST_BASE_VIDEO_CODEC (base_video_encoder)); frame->events = base_video_encoder->current_frame_events; base_video_encoder->current_frame_events = NULL; frame->sink_buffer = buf; frame->presentation_timestamp = GST_BUFFER_TIMESTAMP (buf); frame->presentation_duration = GST_BUFFER_DURATION (buf); frame->presentation_frame_number = base_video_encoder->presentation_frame_number; base_video_encoder->presentation_frame_number++; frame->force_keyframe = base_video_encoder->force_keyframe; base_video_encoder->force_keyframe = FALSE; GST_BASE_VIDEO_CODEC (base_video_encoder)->frames = g_list_append (GST_BASE_VIDEO_CODEC (base_video_encoder)->frames, frame); /* new data, more finish needed */ base_video_encoder->drained = FALSE; GST_LOG_OBJECT (base_video_encoder, "passing frame pfn %d to subclass", frame->presentation_frame_number); ret = klass->handle_frame (base_video_encoder, frame); done: GST_BASE_VIDEO_CODEC_STREAM_UNLOCK (base_video_encoder); g_object_unref (base_video_encoder); return ret; }