static GstFlowReturn
gst_vp8_enc_handle_frame (GstBaseVideoEncoder * base_video_encoder,
    GstVideoFrame * frame)
{
  GstVP8Enc *encoder;
  const GstVideoState *state;
  vpx_codec_err_t status;
  int flags = 0;
  vpx_image_t *image;
  GstVP8EncCoderHook *hook;
  int quality;

  GST_DEBUG_OBJECT (base_video_encoder, "handle_frame");

  encoder = GST_VP8_ENC (base_video_encoder);

  state = gst_base_video_encoder_get_state (base_video_encoder);
  encoder->n_frames++;

  GST_DEBUG_OBJECT (base_video_encoder, "size %d %d", state->width,
      state->height);

  image = gst_vp8_enc_buffer_to_image (encoder, frame->sink_buffer);

  hook = g_slice_new0 (GstVP8EncCoderHook);
  hook->image = image;
  frame->coder_hook = hook;
  frame->coder_hook_destroy_notify =
      (GDestroyNotify) gst_vp8_enc_coder_hook_free;

  if (frame->force_keyframe) {
    flags |= VPX_EFLAG_FORCE_KF;
  }

  quality = (encoder->speed == 0) ? VPX_DL_BEST_QUALITY : VPX_DL_GOOD_QUALITY;

  status = vpx_codec_encode (&encoder->encoder, image,
      encoder->n_frames, 1, flags, quality);
  if (status != 0) {
    GST_ELEMENT_ERROR (encoder, LIBRARY, ENCODE,
        ("Failed to encode frame"), ("%s", gst_vpx_error_name (status)));
    g_slice_free (GstVP8EncCoderHook, hook);
    frame->coder_hook = NULL;
    g_slice_free (vpx_image_t, image);
    return FALSE;
  }

  return gst_vp8_enc_process (encoder);
}
示例#2
0
static gboolean
gst_vp8_enc_handle_frame (GstBaseVideoEncoder * base_video_encoder,
    GstVideoFrame * frame)
{
  GstVP8Enc *encoder;
  const GstVideoState *state;
  vpx_codec_err_t status;
  int flags = 0;
  vpx_codec_iter_t iter = NULL;
  const vpx_codec_cx_pkt_t *pkt;
  vpx_image_t *image;
  GstVP8EncCoderHook *hook;

  GST_DEBUG_OBJECT (base_video_encoder, "handle_frame");

  encoder = GST_VP8_ENC (base_video_encoder);

  state = gst_base_video_encoder_get_state (base_video_encoder);
  encoder->n_frames++;

  GST_DEBUG_OBJECT (base_video_encoder, "size %d %d", state->width,
      state->height);

  if (!encoder->inited) {
    vpx_codec_enc_cfg_t cfg;

    status = vpx_codec_enc_config_default (&vpx_codec_vp8_cx_algo, &cfg, 0);
    if (status != VPX_CODEC_OK) {
      GST_ELEMENT_ERROR (encoder, LIBRARY, INIT,
          ("Failed to get default encoder configuration"), ("%s",
              gst_vpx_error_name (status)));
      return FALSE;
    }

    cfg.g_w = state->width;
    cfg.g_h = state->height;
    cfg.g_timebase.num = state->fps_d;
    cfg.g_timebase.den = state->fps_n;

    cfg.g_error_resilient = encoder->error_resilient;
    cfg.g_lag_in_frames = encoder->max_latency;
    cfg.g_threads = encoder->threads;
    cfg.rc_end_usage = encoder->mode;
    if (encoder->bitrate) {
      cfg.rc_target_bitrate = encoder->bitrate / 1000;
    } else {
      cfg.rc_min_quantizer = 63 - encoder->quality * 5.0;
      cfg.rc_max_quantizer = 63 - encoder->quality * 5.0;
      cfg.rc_target_bitrate = encoder->bitrate;
    }

    cfg.kf_mode = VPX_KF_AUTO;
    cfg.kf_min_dist = 0;
    cfg.kf_max_dist = encoder->max_keyframe_distance;

    cfg.g_pass = encoder->multipass_mode;
    if (encoder->multipass_mode == VPX_RC_FIRST_PASS) {
      encoder->first_pass_cache_content = g_byte_array_sized_new (4096);
    } else if (encoder->multipass_mode == VPX_RC_LAST_PASS) {
      GError *err = NULL;


      if (!encoder->multipass_cache_file) {
        GST_ELEMENT_ERROR (encoder, RESOURCE, OPEN_READ,
            ("No multipass cache file provided"), (NULL));
        return GST_FLOW_ERROR;
      }

      if (!g_file_get_contents (encoder->multipass_cache_file,
              (gchar **) & encoder->last_pass_cache_content.buf,
              &encoder->last_pass_cache_content.sz, &err)) {
        GST_ELEMENT_ERROR (encoder, RESOURCE, OPEN_READ,
            ("Failed to read multipass cache file provided"), ("%s",
                err->message));
        g_error_free (err);
        return GST_FLOW_ERROR;
      }
      cfg.rc_twopass_stats_in = encoder->last_pass_cache_content;
    }

    status = vpx_codec_enc_init (&encoder->encoder, &vpx_codec_vp8_cx_algo,
        &cfg, 0);
    if (status != VPX_CODEC_OK) {
      GST_ELEMENT_ERROR (encoder, LIBRARY, INIT,
          ("Failed to initialize encoder"), ("%s",
              gst_vpx_error_name (status)));
      return GST_FLOW_ERROR;
    }

    status = vpx_codec_control (&encoder->encoder, VP8E_SET_CPUUSED, 0);
    if (status != VPX_CODEC_OK) {
      GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_CPUUSED to 0: %s",
          gst_vpx_error_name (status));
    }

    status =
        vpx_codec_control (&encoder->encoder, VP8E_SET_ENABLEAUTOALTREF,
        (encoder->auto_alt_ref_frames ? 1 : 0));
    if (status != VPX_CODEC_OK) {
      GST_WARNING_OBJECT (encoder,
          "Failed to set VP8E_ENABLEAUTOALTREF to %d: %s",
          (encoder->auto_alt_ref_frames ? 1 : 0), gst_vpx_error_name (status));
    }

    gst_base_video_encoder_set_latency (base_video_encoder, 0,
        gst_util_uint64_scale (encoder->max_latency,
            state->fps_d * GST_SECOND, state->fps_n));
    encoder->inited = TRUE;
  }

  image = gst_vp8_enc_buffer_to_image (encoder, frame->sink_buffer);

  hook = g_slice_new0 (GstVP8EncCoderHook);
  hook->image = image;
  frame->coder_hook = hook;

  if (encoder->force_keyframe) {
    flags |= VPX_EFLAG_FORCE_KF;
  }

  status = vpx_codec_encode (&encoder->encoder, image,
      encoder->n_frames, 1, flags, speed_table[encoder->speed]);
  if (status != 0) {
    GST_ELEMENT_ERROR (encoder, LIBRARY, ENCODE,
        ("Failed to encode frame"), ("%s", gst_vpx_error_name (status)));
    g_slice_free (GstVP8EncCoderHook, hook);
    frame->coder_hook = NULL;
    g_slice_free (vpx_image_t, image);
    return FALSE;
  }

  pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter);
  while (pkt != NULL) {
    GstBuffer *buffer;
    gboolean invisible;

    GST_DEBUG_OBJECT (encoder, "packet %u type %d", (guint) pkt->data.frame.sz,
        pkt->kind);

    if (pkt->kind == VPX_CODEC_STATS_PKT
        && encoder->multipass_mode == VPX_RC_FIRST_PASS) {
      GST_LOG_OBJECT (encoder, "handling STATS packet");

      g_byte_array_append (encoder->first_pass_cache_content,
          pkt->data.twopass_stats.buf, pkt->data.twopass_stats.sz);

      frame = gst_base_video_encoder_get_oldest_frame (base_video_encoder);
      if (frame != NULL) {
        buffer = gst_buffer_new ();
        GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_PREROLL);
        frame->src_buffer = buffer;
        gst_base_video_encoder_finish_frame (base_video_encoder, frame);
      }

      pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter);
      continue;
    } else if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) {
      GST_LOG_OBJECT (encoder, "non frame pkt: %d", pkt->kind);
      pkt = vpx_codec_get_cx_data (&encoder->encoder, &iter);
      continue;
    }

    invisible = (pkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE) != 0;
    frame = gst_base_video_encoder_get_oldest_frame (base_video_encoder);
    g_assert (frame != NULL);
    frame->is_sync_point = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
    hook = frame->coder_hook;

    buffer = gst_buffer_new_and_alloc (pkt->data.frame.sz);

    memcpy (GST_BUFFER_DATA (buffer), pkt->data.frame.buf, pkt->data.frame.sz);

    if (hook->image)
      g_slice_free (vpx_image_t, hook->image);
    hook->image = NULL;

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

  return TRUE;
}