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:
Sebastian Dröge 2015-03-02 15:02:20 +01:00
parent 501a53b26d
commit 51ebca3c03
4 changed files with 72 additions and 19 deletions

View file

@ -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");
encoder = GST_VP8_ENC (video_encoder);
if (encoder->inited) {
ret = gst_vp8_enc_drain (video_encoder); 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);

View file

@ -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;

View file

@ -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");
encoder = GST_VP9_ENC (video_encoder);
if (encoder->inited) {
ret = gst_vp9_enc_drain (video_encoder); 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);

View file

@ -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;
}; };