Exemplo n.º 1
0
static gboolean
theora_enc_write_multipass_cache (GstTheoraEnc * enc, gboolean begin,
    gboolean eos)
{
  GError *err = NULL;
  GIOStatus stat = G_IO_STATUS_NORMAL;
  gint bytes_read = 0;
  gsize bytes_written = 0;
  gchar *buf;

  if (begin) {
    stat = g_io_channel_seek_position (enc->multipass_cache_fd, 0, G_SEEK_SET,
        &err);

    if (stat == G_IO_STATUS_ERROR) {
      if (eos)
        GST_ELEMENT_WARNING (enc, RESOURCE, WRITE, (NULL),
            ("Failed to seek to beginning of multipass cache file: %s",
                err->message));
      else
        GST_ELEMENT_ERROR (enc, RESOURCE, WRITE, (NULL),
            ("Failed to seek to beginning of multipass cache file: %s",
                err->message));
      g_error_free (err);
      return FALSE;
    }
  }


  do {
    bytes_read =
        th_encode_ctl (enc->encoder, TH_ENCCTL_2PASS_OUT, &buf, sizeof (buf));
    if (bytes_read > 0)
      g_io_channel_write_chars (enc->multipass_cache_fd, buf, bytes_read,
          &bytes_written, &err);
  } while (bytes_read > 0 && bytes_written > 0 && !err);

  if (bytes_read < 0 || err) {
    if (bytes_read < 0) {
      GST_ELEMENT_ERROR (enc, RESOURCE, WRITE, (NULL),
          ("Failed to read multipass cache data: %d", bytes_read));
    } else {
      GST_ELEMENT_ERROR (enc, RESOURCE, WRITE, (NULL),
          ("Failed to write multipass cache file: %s", err->message));
    }
    if (err)
      g_error_free (err);

    return FALSE;
  }

  return TRUE;
}
Exemplo n.º 2
0
static gboolean
theora_enc_read_multipass_cache (GstTheoraEnc * enc)
{
  GstBuffer *cache_buf;
  const guint8 *cache_data;
  gsize bytes_read = 0;
  gint bytes_consumed = 0;
  GIOStatus stat = G_IO_STATUS_NORMAL;
  gboolean done = FALSE;

  while (!done) {
    if (gst_adapter_available (enc->multipass_cache_adapter) == 0) {
      cache_buf = gst_buffer_new_and_alloc (512);
      stat = g_io_channel_read_chars (enc->multipass_cache_fd,
          (gchar *) GST_BUFFER_DATA (cache_buf), GST_BUFFER_SIZE (cache_buf),
          &bytes_read, NULL);

      if (bytes_read <= 0) {
        gst_buffer_unref (cache_buf);
        break;
      } else {
        GST_BUFFER_SIZE (cache_buf) = bytes_read;

        gst_adapter_push (enc->multipass_cache_adapter, cache_buf);
      }
    }
    if (gst_adapter_available (enc->multipass_cache_adapter) == 0)
      break;

    bytes_read =
        MIN (gst_adapter_available (enc->multipass_cache_adapter), 512);

    cache_data = gst_adapter_peek (enc->multipass_cache_adapter, bytes_read);

    bytes_consumed =
        th_encode_ctl (enc->encoder, TH_ENCCTL_2PASS_IN, (guint8 *) cache_data,
        bytes_read);
    done = bytes_consumed <= 0;
    if (bytes_consumed > 0)
      gst_adapter_flush (enc->multipass_cache_adapter, bytes_consumed);
  }

  if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_EOF && bytes_read == 0)
      || bytes_consumed < 0) {
    GST_ELEMENT_ERROR (enc, RESOURCE, READ, (NULL),
        ("Failed to read multipass cache file"));
    return FALSE;
  }
  return TRUE;
}
Exemplo n.º 3
0
static int set_video_pass_theora(void * data, int pass, int total_passes,
                                 const char * stats_file)
  {
  char * buf;
  int ret;
  theora_t * theora = data;

  theora->pass = pass;

  if(theora->pass == 1)
    {
    theora->stats_file = fopen(stats_file, "wb");
    /* Get initial header */

    if(!theora->stats_file)
      {
      bg_log(BG_LOG_ERROR, LOG_DOMAIN, "couldn't open stats file %s", stats_file);
      return 0;
      }
    
    ret = th_encode_ctl(theora->ts,
                        TH_ENCCTL_2PASS_OUT,
                        &buf, sizeof(buf));

    if(ret < 0)
      {
      bg_log(BG_LOG_ERROR, LOG_DOMAIN, "getting 2 pass header failed");
      return 0;
      }
    fwrite(buf, 1, ret, theora->stats_file);
    }
  else
    {
    if(!(theora->stats_buf = bg_read_file(stats_file, &theora->stats_size)))
      {
      bg_log(BG_LOG_ERROR, LOG_DOMAIN,
             "couldn't open stats file %s", stats_file);
      return 0;
      }
    theora->stats_ptr = theora->stats_buf;
    }
  return 1;
  }
Exemplo n.º 4
0
// libtheora won't read the entire buffer we give it at once, so we have to
// repeatedly submit it...
static int submit_stats(AVCodecContext *avctx)
{
#ifdef TH_ENCCTL_2PASS_IN
    TheoraContext *h = avctx->priv_data;
    int bytes;
    if (!h->stats) {
        if (!avctx->stats_in) {
            av_log(avctx, AV_LOG_ERROR, "No statsfile for second pass\n");
            return AVERROR(EINVAL);
        }
        h->stats_size = strlen(avctx->stats_in) * 3/4;
        h->stats      = av_malloc(h->stats_size);
        if (!h->stats) {
            h->stats_size = 0;
            return AVERROR(ENOMEM);
        }
        h->stats_size = av_base64_decode(h->stats, avctx->stats_in, h->stats_size);
    }
    while (h->stats_size - h->stats_offset > 0) {
        bytes = th_encode_ctl(h->t_state, TH_ENCCTL_2PASS_IN,
                              h->stats + h->stats_offset,
                              h->stats_size - h->stats_offset);
        if (bytes < 0) {
            av_log(avctx, AV_LOG_ERROR, "Error submitting stats\n");
            return AVERROR_EXTERNAL;
        }
        if (!bytes)
            return 0;
        h->stats_offset += bytes;
    }
    return 0;
#else
    av_log(avctx, AV_LOG_ERROR, "libtheora too old to support 2pass\n");
    return AVERROR(ENOSUP);
#endif
}
Exemplo n.º 5
0
int theora_encode_init(theora_state *_te,theora_info *_ci){
  th_api_info *apiinfo;
  th_info      info;
  ogg_uint32_t keyframe_frequency_force;
  /*Allocate our own combined API wrapper/theora_info struct.
    We put them both in one malloc'd block so that when the API wrapper is
     freed, the info struct goes with it.
    This avoids having to figure out whether or not we need to free the info
     struct in either theora_info_clear() or theora_clear().*/
  apiinfo=(th_api_info *)_ogg_malloc(sizeof(*apiinfo));
  if(apiinfo==NULL)return TH_EFAULT;
  /*Make our own copy of the info struct, since its lifetime should be
     independent of the one we were passed in.*/
  *&apiinfo->info=*_ci;
  oc_theora_info2th_info(&info,_ci);
  apiinfo->api.encode=th_encode_alloc(&info);
  if(apiinfo->api.encode==NULL){
    _ogg_free(apiinfo);
    return OC_EINVAL;
  }
  apiinfo->api.clear=(oc_setup_clear_func)th_enc_api_clear;
  /*Provide entry points for ABI compatibility with old decoder shared libs.*/
  _te->internal_encode=(void *)&OC_ENC_DISPATCH_VTBL;
  _te->internal_decode=NULL;
  _te->granulepos=0;
  _te->i=&apiinfo->info;
  _te->i->codec_setup=&apiinfo->api;
  /*Set the precise requested keyframe frequency.*/
  keyframe_frequency_force=_ci->keyframe_auto_p?
   _ci->keyframe_frequency_force:_ci->keyframe_frequency;
  th_encode_ctl(apiinfo->api.encode,
   TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
   &keyframe_frequency_force,sizeof(keyframe_frequency_force));
  /*TODO: Additional codec setup using the extra fields in theora_info.*/
  return 0;
}
Exemplo n.º 6
0
static av_cold int encode_init(AVCodecContext* avc_context)
{
    th_info t_info;
    th_comment t_comment;
    ogg_packet o_packet;
    unsigned int offset;
    TheoraContext *h = avc_context->priv_data;
    uint32_t gop_size = avc_context->gop_size;

    /* Set up the theora_info struct */
    th_info_init(&t_info);
    t_info.frame_width  = FFALIGN(avc_context->width,  16);
    t_info.frame_height = FFALIGN(avc_context->height, 16);
    t_info.pic_width    = avc_context->width;
    t_info.pic_height   = avc_context->height;
    t_info.pic_x        = 0;
    t_info.pic_y        = 0;
    /* Swap numerator and denominator as time_base in AVCodecContext gives the
     * time period between frames, but theora_info needs the framerate.  */
    t_info.fps_numerator   = avc_context->time_base.den;
    t_info.fps_denominator = avc_context->time_base.num;
    if (avc_context->sample_aspect_ratio.num) {
        t_info.aspect_numerator   = avc_context->sample_aspect_ratio.num;
        t_info.aspect_denominator = avc_context->sample_aspect_ratio.den;
    } else {
        t_info.aspect_numerator   = 1;
        t_info.aspect_denominator = 1;
    }

    if (avc_context->color_primaries == AVCOL_PRI_BT470M)
        t_info.colorspace = TH_CS_ITU_REC_470M;
    else if (avc_context->color_primaries == AVCOL_PRI_BT470BG)
        t_info.colorspace = TH_CS_ITU_REC_470BG;
    else
        t_info.colorspace = TH_CS_UNSPECIFIED;

    if (avc_context->pix_fmt == AV_PIX_FMT_YUV420P)
        t_info.pixel_fmt = TH_PF_420;
    else if (avc_context->pix_fmt == AV_PIX_FMT_YUV422P)
        t_info.pixel_fmt = TH_PF_422;
    else if (avc_context->pix_fmt == AV_PIX_FMT_YUV444P)
        t_info.pixel_fmt = TH_PF_444;
    else {
        av_log(avc_context, AV_LOG_ERROR, "Unsupported pix_fmt\n");
        return -1;
    }
    av_pix_fmt_get_chroma_sub_sample(avc_context->pix_fmt,
                                     &h->uv_hshift, &h->uv_vshift);

    if (avc_context->flags & CODEC_FLAG_QSCALE) {
        /* to be constant with the libvorbis implementation, clip global_quality to 0 - 10
           Theora accepts a quality parameter p, which is:
                * 0 <= p <=63
                * an int value
         */
        t_info.quality        = av_clipf(avc_context->global_quality / (float)FF_QP2LAMBDA, 0, 10) * 6.3;
        t_info.target_bitrate = 0;
    } else {
        t_info.target_bitrate = avc_context->bit_rate;
        t_info.quality        = 0;
    }

    /* Now initialise libtheora */
    h->t_state = th_encode_alloc(&t_info);
    if (!h->t_state) {
        av_log(avc_context, AV_LOG_ERROR, "theora_encode_init failed\n");
        return -1;
    }

    h->keyframe_mask = (1 << t_info.keyframe_granule_shift) - 1;
    /* Clear up theora_info struct */
    th_info_clear(&t_info);

    if (th_encode_ctl(h->t_state, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
                      &gop_size, sizeof(gop_size))) {
        av_log(avc_context, AV_LOG_ERROR, "Error setting GOP size\n");
        return -1;
    }

    // need to enable 2 pass (via TH_ENCCTL_2PASS_) before encoding headers
    if (avc_context->flags & CODEC_FLAG_PASS1) {
        if (get_stats(avc_context, 0))
            return -1;
    } else if (avc_context->flags & CODEC_FLAG_PASS2) {
        if (submit_stats(avc_context))
            return -1;
    }

    /*
        Output first header packet consisting of theora
        header, comment, and tables.

        Each one is prefixed with a 16bit size, then they
        are concatenated together into libavcodec's extradata.
    */
    offset = 0;

    /* Headers */
    th_comment_init(&t_comment);

    while (th_encode_flushheader(h->t_state, &t_comment, &o_packet))
        if (concatenate_packet(&offset, avc_context, &o_packet))
            return -1;

    th_comment_clear(&t_comment);

    /* Set up the output AVFrame */
    avc_context->coded_frame = av_frame_alloc();

    return 0;
}
Exemplo n.º 7
0
void CDemoVideoRecorder::Init(int Width, int Height, int FPS, int Format, const char *pName)
{
    m_pSound = Kernel()->RequestInterface<ISound>();
    m_FPS = FPS;
    m_ScreenWidth = Width;
    m_ScreenHeight = Height;
    m_Format = Format;

    if (m_Format == IClient::DEMO_RECORD_FORMAT_OGV)
    {
        ogg_stream_init(&m_TheoraOggStreamState, rand());
        ogg_stream_init(&m_VorbisOggStreamState, rand());

        char aBuf[1024];
        if (str_find_rev(pName, "/"))
            str_format(aBuf, sizeof(aBuf), "%s.ogv", str_find_rev(pName, "/"));
        else if (str_find_rev(aBuf, "\\"))
            str_format(aBuf, sizeof(pName), "%s.ogv", str_find_rev(pName, "\\"));
        else
            str_format(aBuf, sizeof(aBuf), "%s.ogv", pName);
        m_OggFile = io_open(aBuf, IOFLAG_WRITE);

        //thread_sleep(10000);
        vorbis_info_init(&m_VorbisEncodingInfo);
        vorbis_encode_init_vbr(&m_VorbisEncodingInfo, 2, g_Config.m_SndRate, 1.0f); //2 ch - samplerate - quality 1
        vorbis_analysis_init(&m_VorbisState, &m_VorbisEncodingInfo);
        vorbis_block_init(&m_VorbisState, &m_VorbisBlock);

        vorbis_comment_init(&m_VorbisComment);
        ogg_packet header;
        ogg_packet header_comm;
        ogg_packet header_code;
        vorbis_analysis_headerout(&m_VorbisState, &m_VorbisComment, &header, &header_comm, &header_code);
        ogg_stream_packetin(&m_VorbisOggStreamState, &header);
        ogg_stream_packetin(&m_VorbisOggStreamState, &header_comm);
        ogg_stream_packetin(&m_VorbisOggStreamState, &header_code);


        th_info_init(&m_TheoraEncodingInfo);
        m_TheoraEncodingInfo.frame_width = m_ScreenWidth+15&~0xF;
        m_TheoraEncodingInfo.frame_height = m_ScreenHeight+15&~0xF;
        m_TheoraEncodingInfo.pic_width = m_ScreenWidth;
        m_TheoraEncodingInfo.pic_height = m_ScreenHeight;
        m_TheoraEncodingInfo.pic_x = m_TheoraEncodingInfo.frame_width - m_ScreenWidth>>1&~1;
        m_TheoraEncodingInfo.pic_y = m_TheoraEncodingInfo.frame_height - m_ScreenHeight>>1&~1;
        m_TheoraEncodingInfo.colorspace = TH_CS_UNSPECIFIED;
        m_TheoraEncodingInfo.fps_numerator = FPS; //fps
        m_TheoraEncodingInfo.fps_denominator = 1;
        m_TheoraEncodingInfo.aspect_numerator = -1;
        m_TheoraEncodingInfo.aspect_denominator = -1;
        m_TheoraEncodingInfo.pixel_fmt = TH_PF_444;
        m_TheoraEncodingInfo.target_bitrate = (int)(64870*(ogg_int64_t)48000>>16);
        m_TheoraEncodingInfo.quality = 32;
        m_TheoraEncodingInfo.keyframe_granule_shift = 0;
        m_pThreoraContext = th_encode_alloc(&m_TheoraEncodingInfo);
        int arg = TH_RATECTL_CAP_UNDERFLOW;
        th_encode_ctl(m_pThreoraContext, TH_ENCCTL_SET_RATE_FLAGS, &arg, sizeof(arg));
        th_comment CommentHeader;
        ogg_packet OggPacket;
        th_comment_init(&CommentHeader);
        mem_zero(&OggPacket, sizeof(OggPacket));


            //Flush
        //Step 1
        th_encode_flushheader(m_pThreoraContext, &CommentHeader, &OggPacket); // first header

        ogg_stream_packetin(&m_TheoraOggStreamState, &OggPacket);
        //
        ogg_page OggPage;
        ogg_stream_pageout(&m_TheoraOggStreamState, &OggPage);
        io_write(m_OggFile, OggPage.header, OggPage.header_len);
        io_write(m_OggFile, OggPage.body, OggPage.body_len);

        while(1)
        {
            ogg_page OggPage;
            if (ogg_stream_flush(&m_VorbisOggStreamState,&OggPage) == 0)
                break;
            io_write(m_OggFile, OggPage.header, OggPage.header_len);
            io_write(m_OggFile, OggPage.body, OggPage.body_len);
        }

        while(th_encode_flushheader(m_pThreoraContext, &CommentHeader, &OggPacket))
        {
            ogg_stream_packetin(&m_TheoraOggStreamState, &OggPacket);
        }

        ogg_stream_flush(&m_TheoraOggStreamState, &OggPage);
        io_write(m_OggFile, OggPage.header, OggPage.header_len);
        io_write(m_OggFile, OggPage.body, OggPage.body_len);
    }
Exemplo n.º 8
0
static GstFlowReturn
theora_enc_handle_frame (GstVideoEncoder * benc, GstVideoCodecFrame * frame)
{
    GstTheoraEnc *enc;
    ogg_packet op;
    GstClockTime timestamp, running_time;
    GstFlowReturn ret;
    gboolean force_keyframe;

    enc = GST_THEORA_ENC (benc);

    /* we keep track of two timelines.
     * - The timestamps from the incomming buffers, which we copy to the outgoing
     *   encoded buffers as-is. We need to do this as we simply forward the
     *   newsegment events.
     * - The running_time of the buffers, which we use to construct the granulepos
     *   in the packets.
     */
    timestamp = frame->pts;

    /* incoming buffers are clipped, so this should be positive */
    running_time =
        gst_segment_to_running_time (&GST_VIDEO_ENCODER_INPUT_SEGMENT (enc),
                                     GST_FORMAT_TIME, timestamp);
    g_return_val_if_fail (running_time >= 0 || timestamp < 0, GST_FLOW_ERROR);

    GST_OBJECT_LOCK (enc);
    if (enc->bitrate_changed) {
        long int bitrate = enc->video_bitrate;

        th_encode_ctl (enc->encoder, TH_ENCCTL_SET_BITRATE, &bitrate,
                       sizeof (long int));
        enc->bitrate_changed = FALSE;
    }

    if (enc->quality_changed) {
        long int quality = enc->video_quality;

        th_encode_ctl (enc->encoder, TH_ENCCTL_SET_QUALITY, &quality,
                       sizeof (long int));
        enc->quality_changed = FALSE;
    }

    /* see if we need to schedule a keyframe */
    force_keyframe = GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame);
    GST_OBJECT_UNLOCK (enc);

    if (enc->packetno == 0) {
        /* no packets written yet, setup headers */
        GstCaps *caps;
        GstBuffer *buf;
        GList *buffers = NULL;
        int result;
        GstVideoCodecState *state;

        enc->granulepos_offset = 0;
        enc->timestamp_offset = 0;

        GST_DEBUG_OBJECT (enc, "output headers");
        /* Theora streams begin with three headers; the initial header (with
           most of the codec setup parameters) which is mandated by the Ogg
           bitstream spec.  The second header holds any comment fields.  The
           third header holds the bitstream codebook.  We merely need to
           make the headers, then pass them to libtheora one at a time;
           libtheora handles the additional Ogg bitstream constraints */

        /* create the remaining theora headers */
        th_comment_clear (&enc->comment);
        th_comment_init (&enc->comment);

        while ((result =
                    th_encode_flushheader (enc->encoder, &enc->comment, &op)) > 0) {
            buf = theora_enc_buffer_from_header_packet (enc, &op);
            buffers = g_list_prepend (buffers, buf);
        }
        if (result < 0) {
            g_list_foreach (buffers, (GFunc) gst_buffer_unref, NULL);
            g_list_free (buffers);
            goto encoder_disabled;
        }

        buffers = g_list_reverse (buffers);

        /* mark buffers and put on caps */
        caps = gst_caps_new_empty_simple ("video/x-theora");
        caps = theora_set_header_on_caps (caps, buffers);
        state = gst_video_encoder_set_output_state (benc, caps, enc->input_state);

        GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, state->caps);

        gst_video_codec_state_unref (state);

        gst_video_encoder_negotiate (GST_VIDEO_ENCODER (enc));

        gst_video_encoder_set_headers (benc, buffers);

        theora_enc_reset_ts (enc, running_time, frame->presentation_frame_number);
    }

    {
        th_ycbcr_buffer ycbcr;
        gint res;
        GstVideoFrame vframe;

        if (force_keyframe) {
            theora_enc_reset (enc);
            theora_enc_reset_ts (enc, running_time, frame->presentation_frame_number);
        }

        if (enc->multipass_cache_fd
                && enc->multipass_mode == MULTIPASS_MODE_SECOND_PASS) {
            if (!theora_enc_read_multipass_cache (enc)) {
                ret = GST_FLOW_ERROR;
                goto multipass_read_failed;
            }
        }

        gst_video_frame_map (&vframe, &enc->input_state->info, frame->input_buffer,
                             GST_MAP_READ);
        theora_enc_init_buffer (ycbcr, &vframe);

        res = th_encode_ycbcr_in (enc->encoder, ycbcr);
        gst_video_frame_unmap (&vframe);

        /* none of the failure cases can happen here */
        g_assert (res == 0);

        if (enc->multipass_cache_fd
                && enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS) {
            if (!theora_enc_write_multipass_cache (enc, FALSE, FALSE)) {
                ret = GST_FLOW_ERROR;
                goto multipass_write_failed;
            }
        }

        ret = GST_FLOW_OK;
        while (th_encode_packetout (enc->encoder, 0, &op)) {
            ret = theora_push_packet (enc, &op);
            if (ret != GST_FLOW_OK)
                goto beach;
        }
    }

beach:
    gst_video_codec_frame_unref (frame);
    return ret;

    /* ERRORS */
multipass_read_failed:
    {
        gst_video_codec_frame_unref (frame);
        return ret;
    }
multipass_write_failed:
    {
        gst_video_codec_frame_unref (frame);
        return ret;
    }
encoder_disabled:
    {
        gst_video_codec_frame_unref (frame);
        GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL),
                           ("libtheora has been compiled with the encoder disabled"));
        return GST_FLOW_ERROR;
    }
}
Exemplo n.º 9
0
static void
theora_enc_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstTheoraEnc *enc = GST_THEORA_ENC (object);

  switch (prop_id) {
    case PROP_CENTER:
    case PROP_BORDER:
    case PROP_QUICK:
    case PROP_KEYFRAME_THRESHOLD:
    case PROP_KEYFRAME_MINDISTANCE:
    case PROP_NOISE_SENSITIVITY:
    case PROP_SHARPNESS:
      /* kept for API compat, but ignored */
      break;
    case PROP_BITRATE:
      GST_OBJECT_LOCK (enc);
      enc->video_bitrate = g_value_get_int (value) * 1000;
      enc->bitrate_changed = TRUE;
      GST_OBJECT_UNLOCK (enc);
      break;
    case PROP_QUALITY:
      GST_OBJECT_LOCK (enc);
      if (GST_STATE (enc) >= GST_STATE_PAUSED && enc->video_bitrate > 0) {
        GST_WARNING_OBJECT (object, "Can't change from bitrate to quality mode"
            " while playing");
      } else {
        enc->video_quality = g_value_get_int (value);
        enc->video_bitrate = 0;
        enc->quality_changed = TRUE;
      }
      GST_OBJECT_UNLOCK (enc);
      break;
    case PROP_KEYFRAME_AUTO:
      enc->keyframe_auto = g_value_get_boolean (value);
      break;
    case PROP_KEYFRAME_FREQ:
      enc->keyframe_freq = g_value_get_int (value);
      break;
    case PROP_KEYFRAME_FREQ_FORCE:
      enc->keyframe_force = g_value_get_int (value);
      break;
    case PROP_SPEEDLEVEL:
      enc->speed_level = g_value_get_int (value);
      if (enc->encoder) {
        th_encode_ctl (enc->encoder, TH_ENCCTL_SET_SPLEVEL, &enc->speed_level,
            sizeof (enc->speed_level));
      }
      break;
    case PROP_VP3_COMPATIBLE:
      enc->vp3_compatible = g_value_get_boolean (value);
      break;
    case PROP_DROP_FRAMES:
      enc->drop_frames = g_value_get_boolean (value);
      break;
    case PROP_CAP_OVERFLOW:
      enc->cap_overflow = g_value_get_boolean (value);
      break;
    case PROP_CAP_UNDERFLOW:
      enc->cap_underflow = g_value_get_boolean (value);
      break;
    case PROP_RATE_BUFFER:
      enc->rate_buffer = g_value_get_int (value);
      break;
    case PROP_MULTIPASS_CACHE_FILE:
      enc->multipass_cache_file = g_value_dup_string (value);
      break;
    case PROP_MULTIPASS_MODE:
      enc->multipass_mode = g_value_get_enum (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}
Exemplo n.º 10
0
static GstFlowReturn
theora_enc_chain (GstPad * pad, GstBuffer * buffer)
{
  GstTheoraEnc *enc;
  ogg_packet op;
  GstClockTime timestamp, duration, running_time;
  GstFlowReturn ret;
  gboolean force_keyframe;

  enc = GST_THEORA_ENC (GST_PAD_PARENT (pad));

  /* we keep track of two timelines.
   * - The timestamps from the incomming buffers, which we copy to the outgoing
   *   encoded buffers as-is. We need to do this as we simply forward the
   *   newsegment events.
   * - The running_time of the buffers, which we use to construct the granulepos
   *   in the packets.
   */
  timestamp = GST_BUFFER_TIMESTAMP (buffer);
  duration = GST_BUFFER_DURATION (buffer);

  running_time =
      gst_segment_to_running_time (&enc->segment, GST_FORMAT_TIME, timestamp);
  if ((gint64) running_time < 0) {
    GST_DEBUG_OBJECT (enc, "Dropping buffer, timestamp: %" GST_TIME_FORMAT,
        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
    gst_buffer_unref (buffer);
    return GST_FLOW_OK;
  }

  GST_OBJECT_LOCK (enc);
  if (enc->bitrate_changed) {
    long int bitrate = enc->video_bitrate;

    th_encode_ctl (enc->encoder, TH_ENCCTL_SET_BITRATE, &bitrate,
        sizeof (long int));
    enc->bitrate_changed = FALSE;
  }

  if (enc->quality_changed) {
    long int quality = enc->video_quality;

    th_encode_ctl (enc->encoder, TH_ENCCTL_SET_QUALITY, &quality,
        sizeof (long int));
    enc->quality_changed = FALSE;
  }

  /* see if we need to schedule a keyframe */
  force_keyframe = enc->force_keyframe;
  enc->force_keyframe = FALSE;
  GST_OBJECT_UNLOCK (enc);

  if (force_keyframe) {
    GstClockTime stream_time;
    GstStructure *s;

    stream_time = gst_segment_to_stream_time (&enc->segment,
        GST_FORMAT_TIME, timestamp);

    s = gst_structure_new ("GstForceKeyUnit",
        "timestamp", G_TYPE_UINT64, timestamp,
        "stream-time", G_TYPE_UINT64, stream_time,
        "running-time", G_TYPE_UINT64, running_time, NULL);

    theora_enc_force_keyframe (enc);

    gst_pad_push_event (enc->srcpad,
        gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s));
  }

  /* make sure we copy the discont flag to the next outgoing buffer when it's
   * set on the incomming buffer */
  if (GST_BUFFER_IS_DISCONT (buffer)) {
    enc->next_discont = TRUE;
  }

  if (enc->packetno == 0) {
    /* no packets written yet, setup headers */
    GstCaps *caps;
    GstBuffer *buf;
    GSList *buffers = NULL;
    int result;

    enc->granulepos_offset = 0;
    enc->timestamp_offset = 0;

    GST_DEBUG_OBJECT (enc, "output headers");
    /* Theora streams begin with three headers; the initial header (with
       most of the codec setup parameters) which is mandated by the Ogg
       bitstream spec.  The second header holds any comment fields.  The
       third header holds the bitstream codebook.  We merely need to
       make the headers, then pass them to libtheora one at a time;
       libtheora handles the additional Ogg bitstream constraints */

    /* create the remaining theora headers */
    th_comment_clear (&enc->comment);
    th_comment_init (&enc->comment);

    while ((result =
            th_encode_flushheader (enc->encoder, &enc->comment, &op)) > 0) {
      ret =
          theora_buffer_from_packet (enc, &op, GST_CLOCK_TIME_NONE,
          GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE, &buf);
      if (ret != GST_FLOW_OK) {
        goto header_buffer_alloc;
      }
      buffers = g_slist_prepend (buffers, buf);
    }
    if (result < 0) {
      g_slist_foreach (buffers, (GFunc) gst_buffer_unref, NULL);
      g_slist_free (buffers);
      goto encoder_disabled;
    }

    buffers = g_slist_reverse (buffers);

    /* mark buffers and put on caps */
    caps = gst_pad_get_caps (enc->srcpad);
    caps = theora_set_header_on_caps (caps, buffers);
    GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
    gst_pad_set_caps (enc->srcpad, caps);

    g_slist_foreach (buffers, (GFunc) gst_buffer_set_caps, caps);

    gst_caps_unref (caps);

    /* push out the header buffers */
    while (buffers) {
      buf = buffers->data;
      buffers = g_slist_delete_link (buffers, buffers);
      if ((ret = theora_push_buffer (enc, buf)) != GST_FLOW_OK) {
        g_slist_foreach (buffers, (GFunc) gst_buffer_unref, NULL);
        g_slist_free (buffers);
        goto header_push;
      }
    }

    enc->granulepos_offset =
        gst_util_uint64_scale (running_time, enc->fps_n,
        GST_SECOND * enc->fps_d);
    enc->timestamp_offset = running_time;
    enc->next_ts = 0;
  }

  {
    th_ycbcr_buffer ycbcr;
    gint res;

    theora_enc_init_buffer (ycbcr, &enc->info, GST_BUFFER_DATA (buffer));

    if (theora_enc_is_discontinuous (enc, running_time, duration)) {
      theora_enc_reset (enc);
      enc->granulepos_offset =
          gst_util_uint64_scale (running_time, enc->fps_n,
          GST_SECOND * enc->fps_d);
      enc->timestamp_offset = running_time;
      enc->next_ts = 0;
      enc->next_discont = TRUE;
    }

    if (enc->multipass_cache_fd
        && enc->multipass_mode == MULTIPASS_MODE_SECOND_PASS) {
      if (!theora_enc_read_multipass_cache (enc)) {
        ret = GST_FLOW_ERROR;
        goto multipass_read_failed;
      }
    }

    res = th_encode_ycbcr_in (enc->encoder, ycbcr);
    /* none of the failure cases can happen here */
    g_assert (res == 0);

    if (enc->multipass_cache_fd
        && enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS) {
      if (!theora_enc_write_multipass_cache (enc, FALSE, FALSE)) {
        ret = GST_FLOW_ERROR;
        goto multipass_write_failed;
      }
    }

    ret = GST_FLOW_OK;
    while (th_encode_packetout (enc->encoder, 0, &op)) {
      GstClockTime next_time;

      next_time = th_granule_time (enc->encoder, op.granulepos) * GST_SECOND;

      ret =
          theora_push_packet (enc, &op, timestamp, enc->next_ts,
          next_time - enc->next_ts);

      enc->next_ts = next_time;
      if (ret != GST_FLOW_OK)
        goto data_push;
    }
    gst_buffer_unref (buffer);
  }

  return ret;

  /* ERRORS */
multipass_read_failed:
  {
    gst_buffer_unref (buffer);
    return ret;
  }
multipass_write_failed:
  {
    gst_buffer_unref (buffer);
    return ret;
  }
header_buffer_alloc:
  {
    gst_buffer_unref (buffer);
    return ret;
  }
header_push:
  {
    gst_buffer_unref (buffer);
    return ret;
  }
data_push:
  {
    gst_buffer_unref (buffer);
    return ret;
  }
encoder_disabled:
  {
    GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL),
        ("libtheora has been compiled with the encoder disabled"));
    gst_buffer_unref (buffer);
    return GST_FLOW_ERROR;
  }
}
Exemplo n.º 11
0
static gboolean
theora_enc_flush (GstVideoEncoder * encoder)
{
  GstTheoraEnc *enc = GST_THEORA_ENC (encoder);
  ogg_uint32_t keyframe_force;
  int rate_flags;


  if (enc->input_state == NULL) {
    GST_INFO_OBJECT (enc, "Not configured yet, returning FALSE");

    return FALSE;
  }

  GST_OBJECT_LOCK (enc);
  enc->info.target_bitrate = enc->video_bitrate;
  enc->info.quality = enc->video_quality;
  enc->bitrate_changed = FALSE;
  enc->quality_changed = FALSE;
  GST_OBJECT_UNLOCK (enc);

  if (enc->encoder)
    th_encode_free (enc->encoder);

  enc->encoder = th_encode_alloc (&enc->info);
  /* We ensure this function cannot fail. */
  g_assert (enc->encoder != NULL);
  th_encode_ctl (enc->encoder, TH_ENCCTL_SET_SPLEVEL, &enc->speed_level,
      sizeof (enc->speed_level));
  th_encode_ctl (enc->encoder, TH_ENCCTL_SET_VP3_COMPATIBLE,
      &enc->vp3_compatible, sizeof (enc->vp3_compatible));

  rate_flags = 0;
  if (enc->drop_frames)
    rate_flags |= TH_RATECTL_DROP_FRAMES;
  if (enc->drop_frames)
    rate_flags |= TH_RATECTL_CAP_OVERFLOW;
  if (enc->drop_frames)
    rate_flags |= TH_RATECTL_CAP_UNDERFLOW;
  th_encode_ctl (enc->encoder, TH_ENCCTL_SET_RATE_FLAGS,
      &rate_flags, sizeof (rate_flags));

  if (enc->rate_buffer) {
    th_encode_ctl (enc->encoder, TH_ENCCTL_SET_RATE_BUFFER,
        &enc->rate_buffer, sizeof (enc->rate_buffer));
  } else {
    /* FIXME */
  }

  keyframe_force = enc->keyframe_auto ?
      enc->keyframe_force : enc->keyframe_freq;
  th_encode_ctl (enc->encoder, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
      &keyframe_force, sizeof (keyframe_force));

  /* Get placeholder data */
  if (enc->multipass_cache_fd
      && enc->multipass_mode == MULTIPASS_MODE_FIRST_PASS)
    theora_enc_write_multipass_cache (enc, TRUE, FALSE);

  return TRUE;
}
Exemplo n.º 12
0
static gavl_video_sink_t *
init_theora(void * data, gavl_compression_info_t * ci,
            gavl_video_format_t * format,
            gavl_metadata_t * stream_metadata)
  {
  int sub_h, sub_v;
  int arg_i1, arg_i2;
  uint8_t * ptr;
  ogg_packet op;
  int header_packets;
  
  theora_t * theora = data;

  theora->format = format;

  bg_encoder_set_framerate(&theora->fr, format);
  
  /* Set video format */
  theora->ti.frame_width  = ((format->image_width  + 15)/16*16);
  theora->ti.frame_height = ((format->image_height + 15)/16*16);
  theora->ti.pic_width = format->image_width;
  theora->ti.pic_height = format->image_height;
  
  theora->ti.fps_numerator      = format->timescale;
  theora->ti.fps_denominator    = format->frame_duration;
  theora->ti.aspect_numerator   = format->pixel_width;
  theora->ti.aspect_denominator = format->pixel_height;

  format->interlace_mode = GAVL_INTERLACE_NONE;
    
  format->frame_width  = theora->ti.frame_width;
  format->frame_height = theora->ti.frame_height;
  
  if(theora->cbr)
    theora->ti.quality = 0;
  else
    theora->ti.target_bitrate = 0;

  /* Granule shift */
  theora->ti.keyframe_granule_shift = 0;

  while(1 << theora->ti.keyframe_granule_shift < theora->max_keyframe_interval)
    theora->ti.keyframe_granule_shift++;
  
  theora->ti.colorspace=TH_CS_UNSPECIFIED;

  format->pixelformat =
    gavl_pixelformat_get_best(format->pixelformat,
                              supported_pixelformats, NULL);
    
  switch(format->pixelformat)
    {
    case GAVL_YUV_420_P:
      theora->ti.pixel_fmt = TH_PF_420;
      break;
    case GAVL_YUV_422_P:
      theora->ti.pixel_fmt = TH_PF_422;
      break;
    case GAVL_YUV_444_P:
      theora->ti.pixel_fmt = TH_PF_444;
      break;
    default:
      return 0;
    }
  
  /* Initialize encoder */
  if(!(theora->ts = th_encode_alloc(&theora->ti)))
    {
    bg_log(BG_LOG_ERROR, LOG_DOMAIN,  "th_encode_alloc failed");
    return 0;
    }
  /* Build comment (comments are UTF-8, good for us :-) */

  // build_comment(&theora->tc, metadata);

  /* Call encode CTLs */
  
  // Keyframe frequency

  th_encode_ctl(theora->ts,
                TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
                &theora->max_keyframe_interval, sizeof(theora->max_keyframe_interval));

#ifdef THEORA_1_1  
  // Rate flags

  th_encode_ctl(theora->ts,
                TH_ENCCTL_SET_RATE_FLAGS,
                &theora->rate_flags,sizeof(theora->rate_flags));
#endif
  // Maximum speed

  if(th_encode_ctl(theora->ts,
                   TH_ENCCTL_GET_SPLEVEL_MAX,
                   &arg_i1, sizeof(arg_i1)) != TH_EIMPL)
    {
    arg_i2 = (int)((float)arg_i1 * theora->speed + 0.5);

    if(arg_i2 > arg_i1)
      arg_i2 = arg_i1;
    
    th_encode_ctl(theora->ts, TH_ENCCTL_SET_SPLEVEL,
                  &arg_i2, sizeof(arg_i2));
    }
  
  /* Encode initial packets */

  ci->id = GAVL_CODEC_ID_THEORA;
  ci->flags = GAVL_COMPRESSION_HAS_P_FRAMES;
  
  header_packets = 0;

  /* Build global header */
  while(th_encode_flushheader(theora->ts, &theora->tc, &op) > 0)
    {
    gavl_append_xiph_header(&ci->global_header,
                            (int*)&ci->global_header_len,
                            op.packet, op.bytes);
    
    if(header_packets == 1)
      {
      char * vendor;
      int vendor_len;
      
      /* Extract vendor ID */
      ptr = op.packet + 7;
      vendor_len = GAVL_PTR_2_32LE(ptr); ptr += 4;
      vendor = calloc(1, vendor_len + 1);
      memcpy(vendor, ptr, vendor_len);
      gavl_metadata_set_nocpy(stream_metadata, GAVL_META_SOFTWARE, vendor);
      }
    header_packets++;
    }
  
  if(header_packets < 3)
    {
    bg_log(BG_LOG_ERROR, LOG_DOMAIN,
           "Got %d header packets instead of 3", header_packets);
    return 0;
    }

  /* Initialize buffer */
  
  gavl_pixelformat_chroma_sub(theora->format->pixelformat, &sub_h, &sub_v);
  theora->buf[0].width  = theora->format->frame_width;
  theora->buf[0].height = theora->format->frame_height;
  theora->buf[1].width  = theora->format->frame_width  / sub_h;
  theora->buf[1].height = theora->format->frame_height / sub_v;
  theora->buf[2].width  = theora->format->frame_width  / sub_h;
  theora->buf[2].height = theora->format->frame_height / sub_v;
  
  return gavl_video_sink_create(NULL, write_video_frame_theora, theora,
                                theora->format);
  }
Exemplo n.º 13
0
static gavl_sink_status_t
write_video_frame_theora(void * data, gavl_video_frame_t * frame)
  {
  theora_t * theora;
  int result;
  int i;
  ogg_packet op;
  gavl_packet_t gp;
  //  int64_t frame_index;

  //  fprintf(stderr, "Write frame theora\n");
  
  theora = data;
  
  for(i = 0; i < 3; i++)
    {
    theora->buf[i].stride = frame->strides[i];
    theora->buf[i].data   = frame->planes[i];
    }

#ifdef THEORA_1_1
  if(theora->pass == 2)
    {
    /* Input pass data */
    int ret;
    
    while(theora->stats_ptr - theora->stats_buf < theora->stats_size)
      {
      
      ret = th_encode_ctl(theora->ts,
                          TH_ENCCTL_2PASS_IN,
                          theora->stats_ptr,
                          theora->stats_size -
                          (theora->stats_ptr - theora->stats_buf));

      if(ret < 0)
        {
        bg_log(BG_LOG_ERROR, LOG_DOMAIN, "passing 2 pass data failed");
        return GAVL_SINK_ERROR;
        }
      else if(!ret)
        break;
      else
        {
        theora->stats_ptr += ret;
        }
      }
    }
#endif
  
  result = th_encode_ycbcr_in(theora->ts, theora->buf);

#ifdef THEORA_1_1
  /* Output pass data */
  if(theora->pass == 1)
    {
    int ret;
    char * buf;
    ret = th_encode_ctl(theora->ts,
                        TH_ENCCTL_2PASS_OUT,
                        &buf, sizeof(buf));
    if(ret < 0)
      {
      bg_log(BG_LOG_ERROR, LOG_DOMAIN, "getting 2 pass data failed");
      return GAVL_SINK_ERROR;
      }
    fwrite(buf, 1, ret, theora->stats_file);
    }
#endif

  /* Output packet */
  
  if(!th_encode_packetout(theora->ts, 0, &op))
    {
    bg_log(BG_LOG_ERROR, LOG_DOMAIN,
           "Theora encoder produced no packet");
    return GAVL_SINK_ERROR;
    }
  
  gavl_packet_init(&gp);
  bg_ogg_packet_to_gavl(&op, &gp, NULL);
  
  gp.pts      = theora->pts;
  gp.duration = theora->format->frame_duration;

  theora->pts += theora->format->frame_duration;
  
  if(op.bytes && !(op.packet[0] & 0x40)) // Keyframe
    gp.flags |= GAVL_PACKET_TYPE_I | GAVL_PACKET_KEYFRAME;
  else
    gp.flags |= GAVL_PACKET_TYPE_P;
  
#if 0
  fprintf(stderr, "Encoding granulepos: %lld %lld / %d\n",
          op.granulepos,
          op.granulepos >> theora->ti.keyframe_granule_shift,
          op.granulepos & ((1<<theora->ti.keyframe_granule_shift)-1));
#endif
  //  fprintf(stderr, "Write frame theora done\n");
  //  gavl_packet_dump(&gp);
  return gavl_packet_sink_put_packet(theora->psink, &gp);
  }
Exemplo n.º 14
0
static int theora_encode_control(theora_state *_te,int _req,
 void *_buf,size_t _buf_sz){
  return th_encode_ctl(((th_api_wrapper *)_te->i->codec_setup)->encode,
   _req,_buf,_buf_sz);
}