Ejemplo n.º 1
0
static int
noop_test_encode ()
{
  th_info ti;
  th_enc_ctx *te;

  INFO ("+ Initializing th_info struct");
  th_info_init (&ti);

  INFO ("+ Testing encoder context with empty th_info");
  te = th_encode_alloc(&ti);
  if (te != NULL)
    FAIL("td_encode_alloc accepted an unconfigured th_info");

  INFO ("+ Setting 16x16 image size");
  ti.frame_width = 16;
  ti.frame_height = 16;

  INFO ("+ Allocating encoder context");
  te = th_encode_alloc(&ti);
  if (te == NULL)
    FAIL("td_encode_alloc returned a null pointer");

  INFO ("+ Clearing th_info struct");
  th_info_clear (&ti);

  INFO ("+ Freeing encoder context");
  th_encode_free(te);

  return 0;
}
Ejemplo n.º 2
0
/*
 * Theora beginning of stream
 */
nsresult
MediaRecorder::SetupTheoraBOS()
{
    int i;
    nsresult rv;
    PRUint32 wr;
    ogg_uint32_t keyframe;

    if (ogg_stream_init(&vState->os, rand())) {
        PR_LOG(log, PR_LOG_NOTICE, ("Failed ogg_stream_init\n"));
        return NS_ERROR_FAILURE;
    }

    th_info_init(&vState->ti);

    /* Must be multiples of 16 */
    vState->ti.frame_width = (params->width + 15) & ~0xF;
    vState->ti.frame_height = (params->height + 15) & ~0xF;
    vState->ti.pic_width = params->width;
    vState->ti.pic_height = params->height;
    vState->ti.pic_x = (vState->ti.frame_width - params->width) >> 1 & ~1;
    vState->ti.pic_y = (vState->ti.frame_height - params->height) >> 1 & ~1;
    vState->ti.fps_numerator = params->fps_n;
    vState->ti.fps_denominator = params->fps_d;

    /* Are these the right values? */
    keyframe = 64 - 1;
    for (i = 0; keyframe; i++)
        keyframe >>= 1;
    vState->ti.quality = (int)(params->qual * 100);
    vState->ti.colorspace = TH_CS_ITU_REC_470M;
    vState->ti.pixel_fmt = TH_PF_420;
    vState->ti.keyframe_granule_shift = i;

    vState->th = th_encode_alloc(&vState->ti);
    th_info_clear(&vState->ti);

    /* Header init */
    th_comment_init(&vState->tc);
    th_comment_add_tag(&vState->tc, (char *)"ENCODER", (char *)"rainbow");
    if (th_encode_flushheader(
        vState->th, &vState->tc, &vState->op) <= 0) {
        PR_LOG(log, PR_LOG_NOTICE, ("Internal Theora library error\n"));
        return NS_ERROR_FAILURE;
    }
    th_comment_clear(&vState->tc);

    ogg_stream_packetin(&vState->os, &vState->op);
    if (ogg_stream_pageout(&vState->os, &vState->og) != 1) {
        PR_LOG(log, PR_LOG_NOTICE, ("Internal Ogg library error\n"));
        return NS_ERROR_FAILURE;
    }

    rv = WriteData(vState->og.header, vState->og.header_len, &wr);
    rv = WriteData(vState->og.body, vState->og.body_len, &wr);

    return NS_OK;
}
Ejemplo n.º 3
0
TheoraEnc* theoraenc_new (void) {
  
  TheoraEnc* enc = malloc (sizeof (TheoraEnc));

  if (enc == NULL) {
    printf ("ERROR: couldn't allocate encoder in theoraenc_new\n");
  }
  
  enc->info = malloc (sizeof (th_info));
  enc->comment = malloc (sizeof (th_comment));
  
  if (enc->info && enc->comment) {
    th_info_init (enc->info);
    th_comment_init (enc->comment);
  } else {
    printf ("ERROR: couldn't alloc info/comment in theoraenc_new\n");
    free (enc);
    return NULL;    
  }
  
  /* hardcodes based on the example gstreamer v4l2 -> theora pipeline settings */
  enc->info->frame_width = enc_frame_width;
  enc->info->frame_height = enc_frame_height;
  enc->info->pic_width = enc_pic_width;
  enc->info->pic_height = enc_pic_height;
  enc->info->fps_numerator = enc_fps_numerator;
  enc->info->fps_denominator = enc_fps_denominator;
  enc->info->quality = enc_quality;
  enc->info->target_bitrate = enc_target_bitrate;
  enc->info->keyframe_granule_shift = enc_keyframe_granule_shift;
  
  /* stuff that was already hardcoded in gstreamer's theora encoder */
  enc->info->aspect_numerator = 0;
  enc->info->aspect_denominator = 0;
  enc->info->pixel_fmt = TH_PF_420;
  enc->info->colorspace = TH_CS_UNSPECIFIED;
  
  enc->ctx = th_encode_alloc (enc->info);
  if (enc->ctx == NULL) {
    printf ("Couldn't make a theora context in theoraenc_new.\n");
    printf ("The most likely cause is specifying illegal theora encoder settings.\n");
    printf ("Check the libtheora th_info documentation.\n");
    free (enc);
    return NULL;
  }

  enc->postconv_buffer = calloc (PCONV_BUFFER_TOTAL_SIZE, (sizeof (unsigned char)));
  
  if (enc->postconv_buffer == NULL) {
    printf ("ERROR: couldn't alloc reserve buffer in theoraenc_new\n");
    free (enc);
    return NULL;
  }
  
  return enc;
}
Ejemplo n.º 4
0
static void
theora_enc_reset (GstTheoraEnc * enc)
{
    ogg_uint32_t keyframe_force;
    int rate_flags;

    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);
}
Ejemplo n.º 5
0
static char *
theora_enc_get_supported_formats (void)
{
    th_enc_ctx *encoder;
    th_info info;
    struct
    {
        th_pixel_fmt pixelformat;
        const char *fourcc;
    } formats[] = {
        {
            TH_PF_420, "I420"
        }, {
            TH_PF_422, "Y42B"
        }, {
            TH_PF_444, "Y444"
        }
    };
    GString *string = NULL;
    guint i;

    th_info_init (&info);
    info.frame_width = 16;
    info.frame_height = 16;
    info.fps_numerator = 25;
    info.fps_denominator = 1;
    for (i = 0; i < G_N_ELEMENTS (formats); i++) {
        info.pixel_fmt = formats[i].pixelformat;

        encoder = th_encode_alloc (&info);
        if (encoder == NULL)
            continue;

        GST_LOG ("format %s is supported", formats[i].fourcc);
        th_encode_free (encoder);

        if (string == NULL) {
            string = g_string_new (formats[i].fourcc);
        } else {
            g_string_append (string, ", ");
            g_string_append (string, formats[i].fourcc);
        }
    }
    th_info_clear (&info);

    return string == NULL ? NULL : g_string_free (string, FALSE);
}
Ejemplo n.º 6
0
/* Generate a dummy encoder context for use in th_encode_ctl queries */
th_enc_ctx *dummy_encode_ctx(void)
{
    th_enc_ctx *ctx;
    th_info info;

    /* set the minimal video parameters */
    th_info_init(&info);
    info.frame_width=320;
    info.frame_height=240;
    info.fps_numerator=1;
    info.fps_denominator=1;

    /* allocate and initialize a context object */
    ctx = th_encode_alloc(&info);
    if (ctx == NULL) {
        fprintf(stderr, "Error allocating encoder context.\n");
    }

    /* clear the info struct */
    th_info_clear(&info);

    return ctx;
}
Ejemplo n.º 7
0
/* Generate a dummy encoder context for use in th_encode_ctl queries
   Release with th_encode_free()
   This and the next routine from theora/examples/libtheora_info.c */
static th_enc_ctx *
dummy_encode_ctx (void)
{
  th_enc_ctx *ctx;
  th_info info;

  /* set the minimal video parameters */
  th_info_init (&info);
  info.frame_width = 320;
  info.frame_height = 240;
  info.fps_numerator = 1;
  info.fps_denominator = 1;

  /* allocate and initialize a context object */
  ctx = th_encode_alloc (&info);
  if (!ctx)
    GST_WARNING ("Failed to allocate dummy encoder context.");

  /* clear the info struct */
  th_info_clear (&info);

  return ctx;
}
Ejemplo n.º 8
0
void
gobee_theora_resize(int w, int h, int _stride, int sock, struct sockaddr *addr,
    int addrlen)
{
  time_t stamp = time(NULL);

  th_info_init(&tinfo);
  th_comment_init(&tcmnt);

  tinfo.frame_width = w;
  tinfo.frame_height = h;
  tinfo.pic_width = w;
  tinfo.pic_height = h;
  tinfo.pic_y = 0;
  tinfo.pic_x = 0;
  tinfo.fps_numerator = 30;
  tinfo.fps_denominator = 1;
  tinfo.aspect_denominator = 1;
  tinfo.aspect_numerator = 1;
  tinfo.target_bitrate = 200000;
  tinfo.quality = 12;
  tinfo.colorspace = TH_CS_ITU_REC_470BG; //TH_CS_UNSPECIFIED;
  tinfo.pixel_fmt = TH_PF_420;
  tinfo.keyframe_granule_shift = 100;
  tcmnt.vendor = "qqq";

  // recreate encoder
  if (tctx)
    th_encode_free(tctx);
  tctx = th_encode_alloc(&tinfo);

  __gobee_yuv_resize(&s_ycbcr, w, h);
  printf("sending...\n");
  __gobee_theora_send_headers(sock, addr, addrlen, stamp);
  printf("sent...\n");

}
Ejemplo n.º 9
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;
}
Ejemplo n.º 10
0
bool CHolly_Theora_Video::Start()
{
	m_bFrameWaiting = false;

	th_info thInfo;
	th_info_init( &thInfo );

	thInfo.frame_width				= Width();
	thInfo.frame_height				= Height();
	thInfo.pic_width				= Width();
	thInfo.pic_height				= Height();
	thInfo.pic_x					= 0;
	thInfo.pic_y					= 0;
	thInfo.colorspace				= TH_CS_ITU_REC_470BG;
	thInfo.pixel_fmt				= TH_PF_420; // TH_PF_444
	thInfo.target_bitrate			= BitRate() * 1024;
	thInfo.quality					= 42;
	thInfo.keyframe_granule_shift	= 6; // default value
	thInfo.fps_numerator			= FPS();
	thInfo.fps_denominator			= 1;
	thInfo.aspect_numerator			= 1;
	thInfo.aspect_denominator		= 1;

	m_Encoder = th_encode_alloc( &thInfo );
	if ( !m_Encoder )
	{
		return false;
	}

	th_info_clear( &thInfo );

	SetupFrame();
	WriteHeader();

	return true;
}
Ejemplo n.º 11
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;
}
Ejemplo n.º 12
0
    void writeTheora(std::vector<std::vector<uint8_t>> vidframes, std::string writeTo, uint16_t vidWidth, uint16_t vidHeight) {
        // get paddings to nearest multiple of 0x10
        uint32_t padW = 16 - vidWidth % 16;
        uint32_t padH = 16 - vidHeight % 16;
        uint32_t frmWidth = vidWidth + padW;
        uint32_t frmHeight = vidHeight + padH;

        // initialize theora stream
        th_info vidinfo;
        th_info_init(&vidinfo);
        vidinfo.frame_width     = frmWidth;
        vidinfo.frame_height    = frmHeight;
        vidinfo.pic_width       = vidWidth;
        vidinfo.pic_height      = vidHeight;
        vidinfo.pic_x           = 0;
        vidinfo.pic_y           = 0;
        vidinfo.colorspace      = TH_CS_ITU_REC_470M; // what our RGB->YCbCr function operates on
        vidinfo.pixel_fmt       = TH_PF_444;          // we want the bestest video possible, so no decimation
        vidinfo.target_bitrate  = 0;                  // prefer VBR with quality level...
        vidinfo.quality         = 63;                 // ...which we want as high as possible (since we aren't using photographic frames, lossy compression ruins things)
        vidinfo.fps_numerator   = 15;                 // framerate is 15 fps
        vidinfo.fps_denominator = 1;

        // initialize theora encoding context
        th_enc_ctx * videnc = th_encode_alloc(&vidinfo);

        // initialize theora comment
        th_comment vidcomment;
        th_comment_init(&vidcomment);

        // initialize ogg container
        ogg_stream_state vidcont;
        // serial number chosen by fair dice roll
        if (ogg_stream_init(&vidcont, 42)) { // returned -1, thus failed
            std::cerr << "Failed to initialize ogg container :(\n";
            throw 42;
        }

        // get generic ogg packet & page holders
        ogg_packet vidpacket;
        ogg_page vidpage;

        // generic YCbCr frame, and initial data
        const int Y = 0;
        const int Cb = 1;
        const int Cr = 2; // clarity bonuses
        th_ycbcr_buffer rawdata;
        for (auto & i : rawdata) {
            i.width = i.stride = frmWidth;
            i.height = frmHeight;
            i.data = new unsigned char [frmWidth * frmHeight];
        }
        

        // open file for writing
        std::ofstream vidfile; // because god forbid this thing supports an unsigned char unit
        vidfile.open(writeTo, std::ios::binary);

        // factor out the ogg page writing process a bit
        auto writePage = [&](){
            vidfile.write((char*)vidpage.header, vidpage.header_len);
            if (!vidfile) {
                std::cerr << "An error occured in writing Ogg page header to file. Exiting...\n";
                vidfile.close();
                throw 42;
            }

            vidfile.write((char*)vidpage.body, vidpage.body_len);
            if (!vidfile) {
                std::cerr << "An error occured in writing Ogg page body to file. Exiting...\n";
                vidfile.close();
                throw 42;
            }
        };




        // send header packets to ogg stream
        bool gotone = false;
        while (true) {
            int mkpacket = th_encode_flushheader(videnc, &vidcomment, &vidpacket);
            if (mkpacket == 0) {
                if (gotone) {
                    break;
                } else {
                    std::cerr << "Theora didn't return any header packets.\n";
                    throw 42;
                }
            }

            if (mkpacket < 0) {
                std::cerr << "Theora header flushing failed with error code " << mkpacket << ". Exiting...\n";
                throw 42;
            }

            if (ogg_stream_packetin(&vidcont, &vidpacket)) {
                std::cerr << "Giving packet to Ogg failed, sorry.\n";
                throw 42;
            }

            gotone = true;
        }

        // write ogg pages (and then the remainder via flush) to file
        while (ogg_stream_pageout(&vidcont, &vidpage)) {
            writePage();
        }

        while (ogg_stream_flush(&vidcont, &vidpage)) {
            writePage();
        }

        //////////////////////
        // WRITE THE FRAMES //
        //////////////////////

        for (int FRNO = 0; FRNO < vidframes.size(); FRNO++) {
            auto * VFR = &vidframes.at(FRNO);
            // since we set an offset of (0,0) for the picture, we fill up the
            // top and right edges of the frame with junk. This is us filling
            // the top part
            for (int i = 0; i < padH; i++) {
                for (int j = 0; j < frmWidth; j++) {
                    rawdata[Y].data[i * frmWidth + j] = 0;
                    rawdata[Cb].data[i * frmWidth + j] = 0;
                    rawdata[Cr].data[i * frmWidth + j] = 0;
                }
            }

            // now for the picture itself (every row we add more junk to the right
            // of the image)
            int vecAt = 0; // where we are in the VFR vector
            for (int i = 0; i < vidHeight; i++) {
                for (int j = 0; j < vidWidth; j++) {
                    rawdata[Y].data[i * frmWidth + j]  = VFR->at(vecAt); vecAt++;
                    rawdata[Cb].data[i * frmWidth + j] = VFR->at(vecAt); vecAt++;
                    rawdata[Cr].data[i * frmWidth + j] = VFR->at(vecAt); vecAt++;
                }

                // get right-side padding (fill with junk)
                for (int j = vidWidth; j < frmWidth; j++) {
                    rawdata[Y].data[i * frmWidth + j]  = 0;
                    rawdata[Cb].data[i * frmWidth + j] = 0;
                    rawdata[Cr].data[i * frmWidth + j] = 0;
                }
            }

            // frame made, send through theora
            if (th_encode_ycbcr_in(videnc, rawdata)) {
                std::cerr << "Error in sending frame " << FRNO + 1 << " of " << vidframes.size() << " to Theora.\n";
                throw 42;
            }

            // send theora packets into ogg
            while (true) {
                int packok = th_encode_packetout(videnc, FRNO + 1 == vidframes.size(), &vidpacket);
                if (packok == 0) { break; }
                if (packok < 0) {
                    std::cerr << "Retrieving packet from Theora failed with error code " << packok << ".\n";
                    throw 42;
                }

                if (ogg_stream_packetin(&vidcont, &vidpacket)) {
                    std::cerr << "Giving frame packet to Ogg failed.\n";
                    throw 42;
                }
            }

            // send complete pages from frame to file (we won't flush until
            // after all frames are accounted for, to avoid an abundance of
            // undersized pages)

            while (ogg_stream_pageout(&vidcont, &vidpage)) {
                writePage();
            }
        }

        // take care of any remaining undersized page(s)

        while (ogg_stream_flush(&vidcont, &vidpage)) {
            writePage();
        }

        //// Free/close/etc all relevant structures

        // fstream
        vidfile.close();

        // theora items
        //th_encode_free(videnc); // causes a corrupted double-linked list, somehow, so you'll have to live with unfree'd memory :(
        th_info_clear(&vidinfo);
        th_comment_clear(&vidcomment);

        for (auto & i : rawdata) {
            delete[] i.data;
        }

        // ogg items
        ogg_packet_clear(&vidpacket);
        ogg_stream_clear(&vidcont);
    }
Ejemplo n.º 13
0
krad_theora_encoder_t *krad_theora_encoder_create (int width, int height,
        int fps_numerator,
        int fps_denominator,
        int color_depth,
        int quality) {

    krad_theora_encoder_t *krad_theora;

    krad_theora = calloc (1, sizeof(krad_theora_encoder_t));

    krad_theora->width = width;
    krad_theora->height = height;
    krad_theora->quality = quality;
    krad_theora->color_depth = color_depth;

    th_info_init (&krad_theora->info);
    th_comment_init (&krad_theora->comment);

    krad_theora->info.frame_width = krad_theora->width;
    if (krad_theora->width % 16) {
        krad_theora->info.frame_width += 16 - (krad_theora->width % 16);
    }
    krad_theora->info.frame_height = krad_theora->height;
    if (krad_theora->height % 16) {
        krad_theora->info.frame_height += 16 - (krad_theora->height % 16);
    }
    krad_theora->info.pic_width = krad_theora->width;
    krad_theora->info.pic_height = krad_theora->height;
    krad_theora->info.pic_x = 0;
    krad_theora->info.pic_y = 0;
    krad_theora->info.aspect_denominator = 1;
    krad_theora->info.aspect_numerator = 1;
    krad_theora->info.target_bitrate = 0;
    krad_theora->info.quality = krad_theora->quality;
    if (krad_theora->color_depth == PIX_FMT_YUV420P) {
        krad_theora->info.pixel_fmt = TH_PF_420;
    }
    if (krad_theora->color_depth == PIX_FMT_YUV422P) {
        krad_theora->info.pixel_fmt = TH_PF_422;
    }
    if (krad_theora->color_depth == PIX_FMT_YUV444P) {
        krad_theora->info.pixel_fmt = TH_PF_444;
    }
    krad_theora->keyframe_shift = krad_theora->info.keyframe_granule_shift;

    printk ("Loading Theora encoder version %s", th_version_string());
    printk ("Theora keyframe shift %d", krad_theora->keyframe_shift);

    krad_theora->info.fps_numerator = fps_numerator;
    krad_theora->info.fps_denominator = fps_denominator;
    //krad_theora->keyframe_distance = 30;

    krad_theora->encoder = th_encode_alloc (&krad_theora->info);

    //th_encode_ctl (krad_theora->encoder,
    //TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
    // &krad_theora->keyframe_distance, sizeof(int));
    //printk ("Theora keyframe max distance %u\n",
    //  krad_theora->keyframe_distance);

    /*
    if (strstr(krad_system_cpu_type(), "x86") == NULL) {
      //FOR ARM Realtime
      th_encode_ctl (krad_theora->encoder,
                     TH_ENCCTL_GET_SPLEVEL_MAX,
                     &krad_theora->speed,
                     sizeof(int));
      printk ("Theora encoder speed: %d quality: %d",
              krad_theora->speed, krad_theora->quality);
      th_encode_ctl (krad_theora->encoder, TH_ENCCTL_SET_SPLEVEL,
                     &krad_theora->speed, sizeof(int));
    } else {
      //FOR x86 Realtime
      th_encode_ctl (krad_theora->encoder,
                     TH_ENCCTL_GET_SPLEVEL_MAX,
                     &krad_theora->speed, sizeof(int));
      if ((krad_theora->width <= 1280) && (krad_theora->height <= 720)) {
        krad_theora->speed -= 1;
      }
      printk ("Theora encoder speed: %d quality: %d",
              krad_theora->speed, krad_theora->quality);
      th_encode_ctl (krad_theora->encoder,
                     TH_ENCCTL_SET_SPLEVEL,
                     &krad_theora->speed, sizeof(int));
    }
    */
    /*
      while (th_encode_flushheader ( krad_theora->encoder,
                                     &krad_theora->comment,
                                     &krad_theora->packet) > 0) {

        krad_theora->header_combined_size += krad_theora->packet.bytes;
        krad_theora->header[krad_theora->header_count] =
          malloc (krad_theora->packet.bytes);
        memcpy (krad_theora->header[krad_theora->header_count],
                krad_theora->packet.packet,
                krad_theora->packet.bytes);
        krad_theora->header_len[krad_theora->header_count] =
          krad_theora->packet.bytes;
        krad_theora->header_count++;

        //printk ("krad_theora_encoder_create th_encode_flushheader got
        // header packet %"PRIi64" which is %ld bytes\n",
        //    krad_theora->packet.packetno, krad_theora->packet.bytes);
      }

      //printk ("krad_theora_encoder_create Got %d header packets\n",
      //      krad_theora->header_count);

      krad_theora->header_combined = calloc (1, krad_theora->header_combined_size + 10);

      krad_theora->header_combined[0] = 0x02;
      krad_theora->header_combined_pos++;

      //printk ("main is %ld\n", vorbis->header_main.bytes);
      if (krad_theora->header_len[0] > 255) {
        failfast ("theora mainheader to long for code");
      }

      krad_theora->demented = krad_theora->header_len[0];
      krad_theora->header_combined[1] = (char)krad_theora->demented;
      krad_theora->header_combined_pos++;

      //printk ("comments is %ld\n", vorbis->header_comments.bytes);
      if (krad_theora->header_len[1] > 255) {
        failfast ("theora comments header to long for code");
      }

      krad_theora->demented = krad_theora->header_len[1];
      krad_theora->header_combined[2] = (char)krad_theora->demented;
      krad_theora->header_combined_pos++;

      krad_theora->header_combined_size += 3;

      memcpy (krad_theora->header_combined + krad_theora->header_combined_pos,
              krad_theora->header[0],
              krad_theora->header_len[0]);
      krad_theora->header_combined_pos += krad_theora->header_len[0];

      //printf("main is %ld bytes headerpos is  %d \n",
      //vorbis->header_main.bytes, vorbis->headerpos);

      memcpy (krad_theora->header_combined + krad_theora->header_combined_pos,
              krad_theora->header[1],
              krad_theora->header_len[1]);
      krad_theora->header_combined_pos += krad_theora->header_len[1];

      //printf("comments is %ld bytes headerpos is  %d \n",
      //vorbis->header_comments.bytes, vorbis->headerpos);

      memcpy (krad_theora->header_combined + krad_theora->header_combined_pos,
              krad_theora->header[2],
              krad_theora->header_len[2]);
      krad_theora->header_combined_pos += krad_theora->header_len[2];
      krad_theora->krad_codec_header.codec = THEORA;
      krad_theora->krad_codec_header.header[0] = krad_theora->header[0];
      krad_theora->krad_codec_header.header_size[0] = krad_theora->header_len[0];
      krad_theora->krad_codec_header.header[1] = krad_theora->header[1];
      krad_theora->krad_codec_header.header_size[1] = krad_theora->header_len[1];
      krad_theora->krad_codec_header.header[2] = krad_theora->header[2];
      krad_theora->krad_codec_header.header_size[2] = krad_theora->header_len[2];

      krad_theora->krad_codec_header.header_combined = krad_theora->header_combined;
      krad_theora->krad_codec_header.header_combined_size = krad_theora->header_combined_size;
      krad_theora->krad_codec_header.header_count = 3;

      krad_theora->ycbcr[0].stride =  krad_theora->info.frame_width;
      krad_theora->ycbcr[0].width =  krad_theora->info.frame_width;
      krad_theora->ycbcr[0].height =  krad_theora->info.frame_height;

      krad_theora->ycbcr[0].data = calloc(1, krad_theora->info.frame_width * krad_theora->info.frame_height);

      if (krad_theora->color_depth == PIX_FMT_YUV420P) {
        krad_theora->ycbcr[1].stride = krad_theora->info.frame_width / 2;
        krad_theora->ycbcr[1].width = krad_theora->info.frame_width / 2;
        krad_theora->ycbcr[1].height = krad_theora->info.frame_height / 2;
        krad_theora->ycbcr[2].stride = krad_theora->info.frame_width / 2;
        krad_theora->ycbcr[2].width = krad_theora->info.frame_width / 2;
        krad_theora->ycbcr[2].height = krad_theora->info.frame_height / 2;

        krad_theora->ycbcr[1].data = calloc(1, ((krad_theora->info.frame_width / 2) * (krad_theora->info.frame_height / 2)));
        krad_theora->ycbcr[2].data = calloc(1, ((krad_theora->info.frame_width / 2) * (krad_theora->info.frame_height / 2)));
      }
      if (krad_theora->color_depth == PIX_FMT_YUV422P) {
        krad_theora->ycbcr[1].stride = krad_theora->info.frame_width / 2;
        krad_theora->ycbcr[1].width =  krad_theora->info.frame_width / 2;
        krad_theora->ycbcr[1].height =  krad_theora->info.frame_height;
        krad_theora->ycbcr[2].stride = krad_theora->info.frame_width / 2;
        krad_theora->ycbcr[2].width = krad_theora->info.frame_width / 2;
        krad_theora->ycbcr[2].height =  krad_theora->info.frame_height;

        krad_theora->ycbcr[1].data = calloc(1, ((krad_theora->info.frame_width / 2) * krad_theora->info.frame_height));
        krad_theora->ycbcr[2].data = calloc(1, ((krad_theora->info.frame_width / 2) * krad_theora->info.frame_height));

      }
      if (krad_theora->color_depth == PIX_FMT_YUV444P) {
        krad_theora->ycbcr[1].stride = krad_theora->info.frame_width;
        krad_theora->ycbcr[1].width = krad_theora->info.frame_width;
        krad_theora->ycbcr[1].height = krad_theora->info.frame_height;
        krad_theora->ycbcr[2].stride = krad_theora->info.frame_width;
        krad_theora->ycbcr[2].width = krad_theora->info.frame_width;
        krad_theora->ycbcr[2].height = krad_theora->info.frame_height;

        krad_theora->ycbcr[1].data = calloc(1, krad_theora->info.frame_width * krad_theora->info.frame_height);
        krad_theora->ycbcr[2].data = calloc(1, krad_theora->info.frame_width * krad_theora->info.frame_height);
      }
    */
    return krad_theora;

}
Ejemplo n.º 14
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);
    }
Ejemplo n.º 15
0
static BGBBTJ_VidCodecCTX *theo_begin_compress(int fcc,
	BGBBTJ_BMPInfoHeader *in, BGBBTJ_BMPInfoHeader *out)
{
	byte tbuf[16384];
	ogg_packet op;
	BGBBTJ_VidCodecCTX *ctx;
	vfw_ctxinfo *info;
	byte *ct;
	int err, sz, xs, ys, xs1, ys1, px, py;

	if((fcc!=RIFF_TAG_THEO) && (fcc!=RIFF_TAG_theo) &&
			(fcc!=RIFF_TAG_ther))
		return(NULL);

	ctx=BGBBTJ_VidCodecCTX_New();
	info=gcalloc(sizeof(vfw_ctxinfo));
	ctx->data=info;

	info->ihead=gcalloc(sizeof(BGBBTJ_BMPInfoHeader));
	memset(info->ihead, 0, sizeof(BGBBTJ_BMPInfoHeader));
	info->ihead->biSize		= sizeof(BGBBTJ_BMPInfoHeader);
	info->ihead->biWidth		= in->biWidth;
	info->ihead->biHeight		= in->biHeight;
	info->ihead->biPlanes		= in->biPlanes;
	info->ihead->biBitCount		= in->biBitCount;
	info->ihead->biCompression	= in->biCompression;
	info->ihead->biSizeImage	=
		in->biWidth*in->biHeight*in->biBitCount/8;

//	out->biCompression=RIFF_TAG_ther;
	out->biCompression=RIFF_TAG_theo;

	info->ohead=gcalloc(sizeof(BGBBTJ_BMPInfoHeader));
	memset(info->ohead, 0, sizeof(BGBBTJ_BMPInfoHeader));
	info->ohead->biSize		= sizeof(BGBBTJ_BMPInfoHeader);
	info->ohead->biWidth		= out->biWidth;
//	info->ohead->biHeight		= -out->biHeight;
	info->ohead->biHeight		= out->biHeight;
	info->ohead->biPlanes		= out->biPlanes;
	info->ohead->biBitCount		= out->biBitCount;
	info->ohead->biCompression	= out->biCompression;
	info->ihead->biSizeImage	=
		out->biWidth*out->biHeight*out->biBitCount/8;

//	info->buffer=malloc(out->width*out->height*out->bpp/8);

	theora_info_init(&(info->ti));
//	if(err<0)printf("theo_begin_compress: A Err=%d\n");

	xs=(out->biWidth+15)&(~15);
	ys=(out->biHeight+15)&(~15);
	xs1=xs>>1;
	ys1=ys>>1;

    px=((xs-out->biWidth)>>1)&(~1);
    py=((ys-out->biHeight)>>1)&(~1);

	info->ti.width = out->biWidth;
	info->ti.height = out->biHeight;
	info->ti.frame_width = xs;
	info->ti.frame_height = ys;
    info->ti.offset_x=px;
	info->ti.offset_y=py;
//	info->ti.pixel_fmt=TH_PF_420;
	info->ti.pixelformat=TH_PF_420;
//	info->ti.quality=56;
	info->ti.quality=63;
	info->ti.colorspace=TH_CS_UNSPECIFIED;

	info->ti.fps_numerator=24;
	info->ti.fps_denominator=1;
	info->ti.aspect_numerator=1;
	info->ti.aspect_denominator=1;

	th_info_init(&(info->thi));
//	if(err<0)printf("theo_begin_compress: B Err=%d\n");

	info->thi.pic_width = out->biWidth;
	info->thi.pic_height = out->biHeight;
	info->thi.frame_width = xs;
	info->thi.frame_height = ys;
    info->thi.pic_x=px;
	info->thi.pic_y=py;
	info->thi.fps_numerator=24;
	info->thi.fps_denominator=1;

	info->thi.aspect_numerator=1;
	info->thi.aspect_denominator=1;
	
	info->thi.colorspace=TH_CS_UNSPECIFIED;
	info->thi.pixel_fmt=TH_PF_420;
//	info->thi.pixelformat=TH_PF_420;
//	info->thi.quality=48;
	info->thi.quality=63;

	if(theora_encode_init(&(info->th), &(info->ti)) != OC_DISABLED)
		{ theora_clear(&(info->th)); }

	info->td=th_encode_alloc(&(info->thi));
	th_info_clear(&(info->thi));
//	if(err<0)printf("theo_begin_compress: C Err=%d\n");

	th_comment_init(&(info->tc));
//	if(err<0)printf("theo_begin_compress: D Err=%d\n");

	ct=tbuf;
	
    err=th_encode_flushheader(info->td, &(info->tc), &op);
	if(err<0)printf("theo_begin_compress: E Err=%d\n");

	ct[0]=(op.bytes>>8)&255;
	ct[1]=op.bytes&255;
	ct+=2;

    memcpy(ct, op.packet, op.bytes);
    ct+=op.bytes;

    while(1)
    {
		err=th_encode_flushheader(info->td, &(info->tc), &op);
		if(err<=0)break;

		ct[0]=(op.bytes>>8)&255;
		ct[1]=op.bytes&255;
		ct+=2;

		memcpy(ct, op.packet, op.bytes);
		ct+=op.bytes;
    }

	if(err<0)printf("theo_begin_compress: F Err=%d\n");

	sz=ct-tbuf;
	ctx->vidStrd=gcalloc(sz);
	ctx->sz_vidStrd=sz;
	memcpy(ctx->vidStrd, tbuf, sz);

	info->ycbbuf[0].width=xs;
	info->ycbbuf[0].height=ys;
	info->ycbbuf[0].stride=xs;
	info->ycbbuf[0].data=malloc(xs*ys);

	info->ycbbuf[1].width=xs1;
	info->ycbbuf[1].height=ys1;
	info->ycbbuf[1].stride=xs1;
	info->ycbbuf[1].data=malloc(xs1*ys1);

	info->ycbbuf[2].width=xs1;
	info->ycbbuf[2].height=ys1;
	info->ycbbuf[2].stride=xs1;
	info->ycbbuf[2].data=malloc(xs1*ys1);

	ctx->compress_frame=&theo_compress_frame;

	return(ctx);
}
Ejemplo n.º 16
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);
  }