encoder: mpeg2: derive profile and level from active coding tools.
authorGwenole Beauchesne <gwenole.beauchesne@intel.com>
Mon, 13 Jan 2014 15:56:04 +0000 (16:56 +0100)
committerGwenole Beauchesne <gwenole.beauchesne@intel.com>
Mon, 13 Jan 2014 16:31:55 +0000 (17:31 +0100)
Automatically derive the minimum profile and level to be used for
encoding, based on the activated coding tools. Improve lookup for
the best suitable level with the new MPEG-2 helper functions.

Also change the default profile to "simple" so that to ensure maximum
compatibility when the stream is decoded.

https://bugzilla.gnome.org/show_bug.cgi?id=719703

gst-libs/gst/vaapi/gstvaapiencoder_mpeg2.c
gst-libs/gst/vaapi/gstvaapiencoder_mpeg2_priv.h

index 6d85f92858218aefb6fc9bc314db8c6c141a9fb2..db0f447786d5137a2e2762f61a4106accfa5a713 100644 (file)
@@ -62,58 +62,73 @@ static void clear_references (GstVaapiEncoderMpeg2 * encoder);
 static void push_reference (GstVaapiEncoderMpeg2 * encoder,
     GstVaapiSurfaceProxy * ref);
 
-static struct
+/* Derives the minimum profile from the active coding tools */
+static gboolean
+ensure_profile (GstVaapiEncoderMpeg2 * encoder)
 {
-  int samplers_per_line;
-  int line_per_frame;
-  int frame_per_sec;
-} mpeg2_upper_samplings[2][3] = {
-  /* *INDENT-OFF* */
-  { { 0, 0, 0},
-    { 720, 576, 30 },
-    { 0, 0, 0 },
-  },
-  { { 352, 288, 30 },
-    { 720, 576, 30 },
-    { 1920, 1152, 60 },
-  }
-  /* *INDENT-ON* */
-};
+  GstVaapiProfile profile;
+
+  /* Always start from "simple" profile for maximum compatibility */
+  profile = GST_VAAPI_PROFILE_MPEG2_SIMPLE;
 
+  /* Main profile coding tools */
+  if (encoder->ip_period > 0)
+    profile = GST_VAAPI_PROFILE_MPEG2_MAIN;
+
+  encoder->profile = profile;
+  encoder->profile_idc = gst_vaapi_utils_mpeg2_get_profile_idc (profile);
+  return TRUE;
+}
+
+/* Derives the minimum level from the current configuration */
 static gboolean
-ensure_sampling_desity (GstVaapiEncoderMpeg2 * encoder)
-{
-  guint p, l;
-  float fps;
-
-  p = encoder->profile;
-  l = encoder->level;
-  fps = GST_VAAPI_ENCODER_FPS_N (encoder) / GST_VAAPI_ENCODER_FPS_D (encoder);
-  if (mpeg2_upper_samplings[p][l].samplers_per_line <
-      GST_VAAPI_ENCODER_WIDTH (encoder)
-      || mpeg2_upper_samplings[p][l].line_per_frame <
-      GST_VAAPI_ENCODER_HEIGHT (encoder)
-      || mpeg2_upper_samplings[p][l].frame_per_sec < fps) {
-    GST_ERROR
-        ("acording to slected profile(%d) and level(%d) the max resolution is %dx%d@%d",
-        p, l, mpeg2_upper_samplings[p][l].samplers_per_line,
-        mpeg2_upper_samplings[p][l].line_per_frame,
-        mpeg2_upper_samplings[p][l].frame_per_sec);
-    return FALSE;
+ensure_level (GstVaapiEncoderMpeg2 * encoder)
+{
+  const GstVideoInfo *const vip = GST_VAAPI_ENCODER_VIDEO_INFO (encoder);
+  const guint fps = (vip->fps_n + vip->fps_d - 1) / vip->fps_d;
+  const guint bitrate = GST_VAAPI_ENCODER_CAST (encoder)->bitrate;
+  const GstVaapiMPEG2LevelLimits *limits_table;
+  guint i, num_limits, num_samples;
+
+  num_samples = gst_util_uint64_scale_int_ceil (vip->width * vip->height,
+      vip->fps_n, vip->fps_d);
+
+  limits_table = gst_vaapi_utils_mpeg2_get_level_limits_table (&num_limits);
+  for (i = 0; i < num_limits; i++) {
+    const GstVaapiMPEG2LevelLimits *const limits = &limits_table[i];
+    if (vip->width <= limits->horizontal_size_value &&
+        vip->height <= limits->vertical_size_value &&
+        fps <= limits->frame_rate_value &&
+        num_samples <= limits->sample_rate &&
+        (!bitrate || bitrate <= limits->bit_rate))
+      break;
   }
+  if (i == num_limits)
+    goto error_unsupported_level;
+
+  encoder->level = limits_table[i].level;
+  encoder->level_idc = limits_table[i].level_idc;
   return TRUE;
+
+  /* ERRORS */
+error_unsupported_level:
+  {
+    GST_ERROR ("failed to find a suitable level matching codec config");
+    return FALSE;
+  }
 }
 
-static gboolean
+/* Derives the profile and level that suits best to the configuration */
+static GstVaapiEncoderStatus
 ensure_profile_and_level (GstVaapiEncoderMpeg2 * encoder)
 {
-  if (encoder->profile == GST_ENCODER_MPEG2_PROFILE_SIMPLE) {
-    /* no  b frames */
-    encoder->ip_period = 0;
-    /* only main level is defined in mpeg2 */
-    encoder->level = GST_VAAPI_ENCODER_MPEG2_LEVEL_MAIN;
-  }
-  return TRUE;
+  if (!ensure_profile (encoder))
+    return GST_VAAPI_ENCODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
+
+  if (!ensure_level (encoder))
+    return GST_VAAPI_ENCODER_STATUS_ERROR_OPERATION_FAILED;
+
+  return GST_VAAPI_ENCODER_STATUS_SUCCESS;
 }
 
 static gboolean
@@ -137,40 +152,6 @@ ensure_bitrate (GstVaapiEncoderMpeg2 * encoder)
   return TRUE;
 }
 
-static unsigned char
-make_profile_and_level_indication (guint32 profile, guint32 level)
-{
-  guint32 p = 4, l = 8;
-
-  switch (profile) {
-    case GST_ENCODER_MPEG2_PROFILE_SIMPLE:
-      p = 5;
-      break;
-    case GST_ENCODER_MPEG2_PROFILE_MAIN:
-      p = 4;
-      break;
-    default:
-      g_assert (0);
-      break;
-  }
-
-  switch (level) {
-    case GST_VAAPI_ENCODER_MPEG2_LEVEL_LOW:
-      l = 10;
-      break;
-    case GST_VAAPI_ENCODER_MPEG2_LEVEL_MAIN:
-      l = 8;
-      break;
-    case GST_VAAPI_ENCODER_MPEG2_LEVEL_HIGH:
-      l = 4;
-      break;
-    default:
-      g_assert (0);
-      break;
-  }
-  return p << 4 | l;
-}
-
 static gboolean
 fill_sequence (GstVaapiEncoderMpeg2 * encoder, GstVaapiEncSequence * sequence)
 {
@@ -199,7 +180,7 @@ fill_sequence (GstVaapiEncoderMpeg2 * encoder, GstVaapiEncSequence * sequence)
   seq_param->vbv_buffer_size = 3;       /* B = 16 * 1024 * vbv_buffer_size */
 
   seq_param->sequence_extension.bits.profile_and_level_indication =
-      make_profile_and_level_indication (encoder->profile, encoder->level);
+      (encoder->profile_idc << 4) | encoder->level_idc;
   seq_param->sequence_extension.bits.progressive_sequence = 1;  /* progressive frame-pictures */
   seq_param->sequence_extension.bits.chroma_format =
       gst_vaapi_utils_mpeg2_get_chroma_format_idc
@@ -251,15 +232,19 @@ fill_picture (GstVaapiEncoderMpeg2 * encoder,
   f_code_x = 0xf;
   f_code_y = 0xf;
   if (pic_param->picture_type != VAEncPictureTypeIntra) {
-    if (encoder->level == GST_VAAPI_ENCODER_MPEG2_LEVEL_LOW) {
-      f_code_x = 7;
-      f_code_y = 4;
-    } else if (encoder->level == GST_VAAPI_ENCODER_MPEG2_LEVEL_MAIN) {
-      f_code_x = 8;
-      f_code_y = 5;
-    } else {
-      f_code_x = 9;
-      f_code_y = 5;
+    switch (encoder->level) {
+      case GST_VAAPI_LEVEL_MPEG2_LOW:
+        f_code_x = 7;
+        f_code_y = 4;
+        break;
+      case GST_VAAPI_LEVEL_MPEG2_MAIN:
+        f_code_x = 8;
+        f_code_y = 5;
+        break;
+      default:                 /* High-1440 and High levels */
+        f_code_x = 9;
+        f_code_y = 5;
+        break;
     }
   }
 
@@ -621,24 +606,6 @@ end:
   return status;
 }
 
-static GstVaapiProfile
-to_vaapi_profile (guint32 profile)
-{
-  GstVaapiProfile p;
-
-  switch (profile) {
-    case GST_ENCODER_MPEG2_PROFILE_SIMPLE:
-      p = GST_VAAPI_PROFILE_MPEG2_SIMPLE;
-      break;
-    case GST_ENCODER_MPEG2_PROFILE_MAIN:
-      p = GST_VAAPI_PROFILE_MPEG2_MAIN;
-      break;
-    default:
-      g_assert (0);
-  }
-  return p;
-}
-
 static void
 set_context_info (GstVaapiEncoder * base_encoder)
 {
@@ -657,7 +624,7 @@ set_context_info (GstVaapiEncoder * base_encoder)
     MAX_SLICE_HDR_SIZE = 8,
   };
 
-  base_encoder->profile = to_vaapi_profile (encoder->profile);
+  base_encoder->profile = encoder->profile;
   base_encoder->num_ref_frames = 2;
 
   /* Only YUV 4:2:0 formats are supported for now. This means that we
@@ -683,17 +650,18 @@ gst_vaapi_encoder_mpeg2_reconfigure (GstVaapiEncoder * base_encoder)
 {
   GstVaapiEncoderMpeg2 *const encoder =
       GST_VAAPI_ENCODER_MPEG2_CAST (base_encoder);
+  GstVaapiEncoderStatus status;
 
   if (encoder->ip_period > base_encoder->keyframe_period) {
     encoder->ip_period = base_encoder->keyframe_period - 1;
   }
 
-  if (!ensure_profile_and_level (encoder))
-    goto error;
+  status = ensure_profile_and_level (encoder);
+  if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS)
+    return status;
+
   if (!ensure_bitrate (encoder))
     goto error;
-  if (!ensure_sampling_desity (encoder))
-    goto error;
 
   set_context_info (base_encoder);
   return GST_VAAPI_ENCODER_STATUS_SUCCESS;
index d36a3e5ea3c96e158c546eaa7828074dac7308d1..f2e02dfaf381ffb350688dbbced862a51a3361b8 100644 (file)
@@ -30,19 +30,6 @@ G_BEGIN_DECLS
 #define GST_VAAPI_ENCODER_MPEG2_CAST(encoder) \
   ((GstVaapiEncoderMpeg2 *) (encoder))
 
-typedef enum
-{
-  GST_ENCODER_MPEG2_PROFILE_SIMPLE,
-  GST_ENCODER_MPEG2_PROFILE_MAIN,
-} GstEncoderMpeg2Level;
-
-typedef enum
-{
-  GST_VAAPI_ENCODER_MPEG2_LEVEL_LOW,
-  GST_VAAPI_ENCODER_MPEG2_LEVEL_MAIN,
-  GST_VAAPI_ENCODER_MPEG2_LEVEL_HIGH
-} GstVaapiEncoderMpeg2Level;
-
 #define START_CODE_PICUTRE      0x00000100
 #define START_CODE_SLICE        0x00000101
 #define START_CODE_USER         0x000001B2
@@ -54,8 +41,10 @@ struct _GstVaapiEncoderMpeg2
 {
   GstVaapiEncoder parent_instance;
 
-  guint32 profile;
-  guint32 level;
+  GstVaapiProfile profile;
+  GstVaapiLevelMPEG2 level;
+  guint8 profile_idc;
+  guint8 level_idc;
   guint32 cqp; /* quantizer value for CQP mode */
   guint32 ip_period;