mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
vp{8,9}enc: Tell the encoder about actual timestamps and durations of frames
... instead of just counting frames. The values are supposed to be in timebase units, not frame units. This fixes various quality problems with VP8/VP9 encoding and in general makes the encoder behave better. Thanks to Nirbheek Chauhan for noticing this bug.
This commit is contained in:
parent
501a53b26d
commit
51ebca3c03
4 changed files with 72 additions and 19 deletions
|
@ -805,7 +805,6 @@ gst_vp8_enc_finalize (GObject * object)
|
||||||
g_mutex_clear (&gst_vp8_enc->encoder_lock);
|
g_mutex_clear (&gst_vp8_enc->encoder_lock);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1877,15 +1876,19 @@ gst_vp8_enc_drain (GstVideoEncoder * video_encoder)
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
vpx_codec_err_t status;
|
vpx_codec_err_t status;
|
||||||
gint64 deadline;
|
gint64 deadline;
|
||||||
|
vpx_codec_pts_t pts;
|
||||||
|
|
||||||
encoder = GST_VP8_ENC (video_encoder);
|
encoder = GST_VP8_ENC (video_encoder);
|
||||||
|
|
||||||
g_mutex_lock (&encoder->encoder_lock);
|
g_mutex_lock (&encoder->encoder_lock);
|
||||||
deadline = encoder->deadline;
|
deadline = encoder->deadline;
|
||||||
|
|
||||||
status =
|
pts =
|
||||||
vpx_codec_encode (&encoder->encoder, NULL, encoder->n_frames, 1, flags,
|
gst_util_uint64_scale (encoder->last_pts,
|
||||||
deadline);
|
encoder->cfg.g_timebase.num * (GstClockTime) GST_SECOND,
|
||||||
|
encoder->cfg.g_timebase.den);
|
||||||
|
|
||||||
|
status = vpx_codec_encode (&encoder->encoder, NULL, pts, 1, flags, deadline);
|
||||||
g_mutex_unlock (&encoder->encoder_lock);
|
g_mutex_unlock (&encoder->encoder_lock);
|
||||||
|
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
|
@ -1917,11 +1920,18 @@ gst_vp8_enc_drain (GstVideoEncoder * video_encoder)
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_vp8_enc_finish (GstVideoEncoder * video_encoder)
|
gst_vp8_enc_finish (GstVideoEncoder * video_encoder)
|
||||||
{
|
{
|
||||||
|
GstVP8Enc *encoder;
|
||||||
GstFlowReturn ret;
|
GstFlowReturn ret;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (video_encoder, "finish");
|
GST_DEBUG_OBJECT (video_encoder, "finish");
|
||||||
|
|
||||||
ret = gst_vp8_enc_drain (video_encoder);
|
encoder = GST_VP8_ENC (video_encoder);
|
||||||
|
|
||||||
|
if (encoder->inited) {
|
||||||
|
ret = gst_vp8_enc_drain (video_encoder);
|
||||||
|
} else {
|
||||||
|
ret = GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1954,13 +1964,13 @@ gst_vp8_enc_handle_frame (GstVideoEncoder * video_encoder,
|
||||||
vpx_image_t *image;
|
vpx_image_t *image;
|
||||||
GstVP8EncUserData *user_data;
|
GstVP8EncUserData *user_data;
|
||||||
GstVideoFrame vframe;
|
GstVideoFrame vframe;
|
||||||
|
vpx_codec_pts_t pts;
|
||||||
|
unsigned long duration;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (video_encoder, "handle_frame");
|
GST_DEBUG_OBJECT (video_encoder, "handle_frame");
|
||||||
|
|
||||||
encoder = GST_VP8_ENC (video_encoder);
|
encoder = GST_VP8_ENC (video_encoder);
|
||||||
|
|
||||||
encoder->n_frames++;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (video_encoder, "size %d %d",
|
GST_DEBUG_OBJECT (video_encoder, "size %d %d",
|
||||||
GST_VIDEO_INFO_WIDTH (&encoder->input_state->info),
|
GST_VIDEO_INFO_WIDTH (&encoder->input_state->info),
|
||||||
GST_VIDEO_INFO_HEIGHT (&encoder->input_state->info));
|
GST_VIDEO_INFO_HEIGHT (&encoder->input_state->info));
|
||||||
|
@ -1979,8 +1989,25 @@ gst_vp8_enc_handle_frame (GstVideoEncoder * video_encoder,
|
||||||
}
|
}
|
||||||
|
|
||||||
g_mutex_lock (&encoder->encoder_lock);
|
g_mutex_lock (&encoder->encoder_lock);
|
||||||
|
pts =
|
||||||
|
gst_util_uint64_scale (frame->pts,
|
||||||
|
encoder->cfg.g_timebase.num * (GstClockTime) GST_SECOND,
|
||||||
|
encoder->cfg.g_timebase.den);
|
||||||
|
encoder->last_pts = frame->pts;
|
||||||
|
|
||||||
|
if (frame->duration != GST_CLOCK_TIME_NONE) {
|
||||||
|
duration =
|
||||||
|
gst_util_uint64_scale (frame->duration,
|
||||||
|
encoder->cfg.g_timebase.num * (GstClockTime) GST_SECOND,
|
||||||
|
encoder->cfg.g_timebase.den);
|
||||||
|
encoder->last_pts += frame->duration;
|
||||||
|
} else {
|
||||||
|
duration = 1;
|
||||||
|
}
|
||||||
|
|
||||||
status = vpx_codec_encode (&encoder->encoder, image,
|
status = vpx_codec_encode (&encoder->encoder, image,
|
||||||
encoder->n_frames, 1, flags, encoder->deadline);
|
pts, duration, flags, encoder->deadline);
|
||||||
|
|
||||||
g_mutex_unlock (&encoder->encoder_lock);
|
g_mutex_unlock (&encoder->encoder_lock);
|
||||||
gst_video_frame_unmap (&vframe);
|
gst_video_frame_unmap (&vframe);
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ struct _GstVP8Enc
|
||||||
|
|
||||||
vpx_image_t image;
|
vpx_image_t image;
|
||||||
|
|
||||||
int n_frames;
|
GstClockTime last_pts;
|
||||||
int keyframe_distance;
|
int keyframe_distance;
|
||||||
|
|
||||||
GstVideoCodecState *input_state;
|
GstVideoCodecState *input_state;
|
||||||
|
|
|
@ -780,7 +780,6 @@ gst_vp9_enc_finalize (GObject * object)
|
||||||
g_mutex_clear (&gst_vp9_enc->encoder_lock);
|
g_mutex_clear (&gst_vp9_enc->encoder_lock);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1816,15 +1815,19 @@ gst_vp9_enc_drain (GstVideoEncoder * video_encoder)
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
vpx_codec_err_t status;
|
vpx_codec_err_t status;
|
||||||
gint64 deadline;
|
gint64 deadline;
|
||||||
|
vpx_codec_pts_t pts;
|
||||||
|
|
||||||
encoder = GST_VP9_ENC (video_encoder);
|
encoder = GST_VP9_ENC (video_encoder);
|
||||||
|
|
||||||
g_mutex_lock (&encoder->encoder_lock);
|
g_mutex_lock (&encoder->encoder_lock);
|
||||||
deadline = encoder->deadline;
|
deadline = encoder->deadline;
|
||||||
|
|
||||||
status =
|
pts =
|
||||||
vpx_codec_encode (&encoder->encoder, NULL, encoder->n_frames, 1, flags,
|
gst_util_uint64_scale (encoder->last_pts,
|
||||||
deadline);
|
encoder->cfg.g_timebase.num * (GstClockTime) GST_SECOND,
|
||||||
|
encoder->cfg.g_timebase.den);
|
||||||
|
|
||||||
|
status = vpx_codec_encode (&encoder->encoder, NULL, pts, 0, flags, deadline);
|
||||||
g_mutex_unlock (&encoder->encoder_lock);
|
g_mutex_unlock (&encoder->encoder_lock);
|
||||||
|
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
|
@ -1856,11 +1859,18 @@ gst_vp9_enc_drain (GstVideoEncoder * video_encoder)
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_vp9_enc_finish (GstVideoEncoder * video_encoder)
|
gst_vp9_enc_finish (GstVideoEncoder * video_encoder)
|
||||||
{
|
{
|
||||||
|
GstVP9Enc *encoder;
|
||||||
GstFlowReturn ret;
|
GstFlowReturn ret;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (video_encoder, "finish");
|
GST_DEBUG_OBJECT (video_encoder, "finish");
|
||||||
|
|
||||||
ret = gst_vp9_enc_drain (video_encoder);
|
encoder = GST_VP9_ENC (video_encoder);
|
||||||
|
|
||||||
|
if (encoder->inited) {
|
||||||
|
ret = gst_vp9_enc_drain (video_encoder);
|
||||||
|
} else {
|
||||||
|
ret = GST_FLOW_OK;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1892,13 +1902,13 @@ gst_vp9_enc_handle_frame (GstVideoEncoder * video_encoder,
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
vpx_image_t *image;
|
vpx_image_t *image;
|
||||||
GstVideoFrame vframe;
|
GstVideoFrame vframe;
|
||||||
|
vpx_codec_pts_t pts;
|
||||||
|
unsigned long duration;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (video_encoder, "handle_frame");
|
GST_DEBUG_OBJECT (video_encoder, "handle_frame");
|
||||||
|
|
||||||
encoder = GST_VP9_ENC (video_encoder);
|
encoder = GST_VP9_ENC (video_encoder);
|
||||||
|
|
||||||
encoder->n_frames++;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (video_encoder, "size %d %d",
|
GST_DEBUG_OBJECT (video_encoder, "size %d %d",
|
||||||
GST_VIDEO_INFO_WIDTH (&encoder->input_state->info),
|
GST_VIDEO_INFO_WIDTH (&encoder->input_state->info),
|
||||||
GST_VIDEO_INFO_HEIGHT (&encoder->input_state->info));
|
GST_VIDEO_INFO_HEIGHT (&encoder->input_state->info));
|
||||||
|
@ -1912,8 +1922,25 @@ gst_vp9_enc_handle_frame (GstVideoEncoder * video_encoder,
|
||||||
}
|
}
|
||||||
|
|
||||||
g_mutex_lock (&encoder->encoder_lock);
|
g_mutex_lock (&encoder->encoder_lock);
|
||||||
|
pts =
|
||||||
|
gst_util_uint64_scale (frame->pts,
|
||||||
|
encoder->cfg.g_timebase.num * (GstClockTime) GST_SECOND,
|
||||||
|
encoder->cfg.g_timebase.den);
|
||||||
|
encoder->last_pts = frame->pts;
|
||||||
|
|
||||||
|
if (frame->duration != GST_CLOCK_TIME_NONE) {
|
||||||
|
duration =
|
||||||
|
gst_util_uint64_scale (frame->duration,
|
||||||
|
encoder->cfg.g_timebase.num * (GstClockTime) GST_SECOND,
|
||||||
|
encoder->cfg.g_timebase.den);
|
||||||
|
encoder->last_pts += frame->duration;
|
||||||
|
} else {
|
||||||
|
duration = 1;
|
||||||
|
}
|
||||||
|
|
||||||
status = vpx_codec_encode (&encoder->encoder, image,
|
status = vpx_codec_encode (&encoder->encoder, image,
|
||||||
encoder->n_frames, 1, flags, encoder->deadline);
|
pts, duration, flags, encoder->deadline);
|
||||||
|
|
||||||
g_mutex_unlock (&encoder->encoder_lock);
|
g_mutex_unlock (&encoder->encoder_lock);
|
||||||
gst_video_frame_unmap (&vframe);
|
gst_video_frame_unmap (&vframe);
|
||||||
|
|
||||||
|
|
|
@ -102,8 +102,7 @@ struct _GstVP9Enc
|
||||||
|
|
||||||
vpx_image_t image;
|
vpx_image_t image;
|
||||||
|
|
||||||
int n_frames;
|
GstClockTime last_pts;
|
||||||
|
|
||||||
GstVideoCodecState *input_state;
|
GstVideoCodecState *input_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue