aboutsummaryrefslogtreecommitdiffstats
path: root/src/kvilib/ext/KviTheoraDecoder.cpp
diff options
context:
space:
mode:
authorGravatar Szymon Tomasz Stefanek2010-12-25 05:28:46 +0000
committerGravatar Szymon Tomasz Stefanek2010-12-25 05:28:46 +0000
commitf2f47c5eaa7d284abad1f693d7c75c85c42565df (patch)
tree2dd141a4ae4911ef3a1abf4d1910d3f7ba071c27 /src/kvilib/ext/KviTheoraDecoder.cpp
parentMore work on the window popup (diff)
downloadKVIrc-f2f47c5eaa7d284abad1f693d7c75c85c42565df.tar.gz
KVIrc-f2f47c5eaa7d284abad1f693d7c75c85c42565df.tar.bz2
KVIrc-f2f47c5eaa7d284abad1f693d7c75c85c42565df.zip
Beginning of 'the big rename & cleanup'. File names should match class names, when possible. Move classes to one per file. Move C functions to namespaces, if possible. Kill some abbreviations...
git-svn-id: https://svn.kvirc.de/svn/trunk/kvirc@5229 17fca916-40b9-46aa-a4ea-0a15b648b75c
Diffstat (limited to 'src/kvilib/ext/KviTheoraDecoder.cpp')
-rw-r--r--src/kvilib/ext/KviTheoraDecoder.cpp653
1 files changed, 653 insertions, 0 deletions
diff --git a/src/kvilib/ext/KviTheoraDecoder.cpp b/src/kvilib/ext/KviTheoraDecoder.cpp
new file mode 100644
index 000000000..eba0ac21b
--- /dev/null
+++ b/src/kvilib/ext/KviTheoraDecoder.cpp
@@ -0,0 +1,653 @@
+//=============================================================================
+//
+// File : KviTheoraDecoder.cpp
+// Creation date : Sat Nov 21 2009 22:53:21 CEST by Fabio Bas
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2009 Fabio Bas (ctrlaltca at libero dot it)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// Based on the example encoder/player from Xiph.Org Foundation:
+//=============================================================================
+//
+// function: example encoder application; makes an Ogg Theora/Vorbis
+// file from YUV4MPEG2 and WAV input
+// last mod: $Id: encoder_example.c 16517 2009-08-25 19:48:57Z giles $
+//
+//=============================================================================
+//
+// function: example SDL player application; plays Ogg Theora files (with
+// optional Vorbis audio second stream)
+// last mod: $Id: player_example.c 16628 2009-10-10 05:50:52Z gmaxwell $
+//
+//=============================================================================
+
+#ifndef COMPILE_DISABLE_OGG_THEORA
+
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <math.h>
+
+#include "KviTheoraDecoder.h"
+#include "KviOggIrcText.h"
+
+#include "KviDataBuffer.h"
+
+using namespace KviOggIrcText;
+
+static void rgb32toyuv444(QRgb * rgbPt, unsigned char *yuvPt, int w, int h)
+{
+ for(int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ yuvPt[i * w + j] = (unsigned char) (( 66 * qRed(*rgbPt) + 129 * qGreen(*rgbPt) + 25 * qBlue(*rgbPt) + 128) >> 8) + 16; // y
+ yuvPt[(i + h) * w + j] = (unsigned char) ((-38 * qRed(*rgbPt) - 74 * qGreen(*rgbPt) + 112 * qBlue(*rgbPt) + 128) >> 8) + 128; // u
+ yuvPt[(i + 2 * h) * w + j] = (unsigned char) ((112 * qRed(*rgbPt) - 94 * qGreen(*rgbPt) - 18 * qBlue(*rgbPt) + 128) >> 8) + 128; // v
+ //this moves the pointer forward 4 bytes (ARGB)
+ rgbPt++;
+ }
+ }
+ return;
+}
+
+KviTheoraEncoder::KviTheoraEncoder(KviDataBuffer * stream, int iWidth, int iHeight, int iFpsN, int iFpsD, int iParN, int iParD)
+{
+ //used to check functions results
+ int ret;
+ videoflag=0;
+ videoYuv=0;
+ frame_state=-1;
+
+ //text
+ text_sofar=0;
+
+ //user settings
+ m_pStream=stream;
+ geometry.pic_w=iWidth;
+ geometry.pic_h=iHeight;
+ //frame per second
+ int video_fps_n=iFpsN;
+ int video_fps_d=iFpsD;
+ //aspect ratio
+ int video_par_n=iParN;
+ int video_par_d=iParD;
+
+
+ // preset settings
+ int video_q=48;
+ ogg_uint32_t keyframe_frequency=64;
+
+ y4m_dst_buf_read_sz = geometry.pic_w * geometry.pic_h * 3;
+ y4m_aux_buf_sz = y4m_aux_buf_read_sz=0;
+
+ // buffer allocations
+ y4m_dst_buf_read_sz = geometry.pic_w * geometry.pic_h * 3;
+ y4m_aux_buf_sz = y4m_aux_buf_read_sz=0;
+ // The size of the final frame buffers is always computed from the destination chroma decimation type.
+ y4m_dst_buf_sz = geometry.pic_w * geometry.pic_h * 3;
+
+ // Set up Ogg output stream
+ srand(time(NULL));
+ ogg_stream_init(&to,rand());
+
+ ogg_stream_init(&zo,rand());
+ ret = irct_encode_init();
+ if(ret)
+ {
+ qDebug("error initializing irctext");
+ return;
+ }
+
+
+ // Set up Theora encoder
+ // Theora has a divisible-by-sixteen restriction for the encoded frame size
+ // scale the picture size up to the nearest /16 and calculate offsets
+ geometry.frame_w = (geometry.pic_w + 15) &~ 0xF;
+ geometry.frame_h = (geometry.pic_h + 15) &~ 0xF;
+ // Force the offsets to be even so that chroma samples line up like we expect.
+ geometry.pic_x = (geometry.frame_w - geometry.pic_w) >> 1 &~ 1;
+ geometry.pic_y = (geometry.frame_h - geometry.pic_h) >> 1 &~ 1;
+
+ // Fill in a th_info structure with details on the format of the video you wish to encode.
+ th_info_init(&ti);
+ ti.frame_width=geometry.frame_w;
+ ti.frame_height=geometry.frame_h;
+ ti.pic_width=geometry.pic_w;
+ ti.pic_height=geometry.pic_h;
+ ti.pic_x=geometry.pic_x;
+ ti.pic_y=geometry.pic_y;
+ ti.fps_numerator=video_fps_n;
+ ti.fps_denominator=video_fps_d;
+ ti.aspect_numerator=video_par_n;
+ ti.aspect_denominator=video_par_d;
+ ti.colorspace=TH_CS_UNSPECIFIED;
+ //use vbr, not cbr
+ ti.target_bitrate=0;
+ ti.quality=video_q;
+ ti.keyframe_granule_shift=ilog(keyframe_frequency-1);
+ ti.pixel_fmt=TH_PF_444;
+ // Allocate a th_enc_ctx handle with th_encode_alloc().
+ td=th_encode_alloc(&ti);
+ if(td==0){
+ qDebug("Could not th_encode_alloc().");
+ return;
+ }
+
+ th_info_clear(&ti);
+ // Perform any additional encoder configuration required with th_encode_ctl().
+ // setting just the granule shift only allows power-of-two keyframe spacing. Set the actual requested spacing.
+ ret=th_encode_ctl(td,TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,&keyframe_frequency,sizeof(keyframe_frequency-1));
+ if(ret<0){
+ qDebug("Could not set keyframe interval to %d.",(int)keyframe_frequency);
+ }
+ // write the bitstream header packets with proper page interleave
+ th_comment_init(&tc);
+
+ // Repeatedly call th_encode_flushheader() to retrieve all the header packets.
+ // first packet will get its own page automatically
+ if(th_encode_flushheader(td,&tc,&op)<=0){
+ qDebug("Internal Theora library error.");
+ return;
+ }
+
+ ogg_stream_packetin(&to,&op);
+ if(ogg_stream_pageout(&to,&og)!=1){
+ qDebug("Internal Ogg library error.");
+ return;
+ }
+
+ m_pStream->append(og.header,og.header_len);
+ m_pStream->append(og.body,og.body_len);
+
+ // create the remaining theora headers
+ for(;;){
+ ret=th_encode_flushheader(td,&tc,&op);
+ if(ret<0){
+ qDebug("Internal Theora library error.");
+ return;
+ }
+ else if(!ret)break;
+ ogg_stream_packetin(&to,&op);
+ }
+
+ //irct
+ ogg_packet header;
+ irct_encode_headerout(&header);
+ ogg_stream_packetin(&zo,&header); // automatically placed in its own page
+ if(ogg_stream_pageout(&zo,&og)!=1){
+ qDebug("Internal Ogg library error (irct).");
+ return;
+ }
+ m_pStream->append(og.header,og.header_len);
+ m_pStream->append(og.body,og.body_len);
+
+ // Flush the rest of our headers. This ensures the actual data in each stream
+ // will start on a new page, as per spec.
+ for(;;)
+ {
+ int result = ogg_stream_flush(&to,&og);
+ if(result<0){
+ // can't get here
+ qDebug("Internal Ogg library error.");
+ return;
+ }
+ if(result==0)break;
+ m_pStream->append(og.header,og.header_len);
+ m_pStream->append(og.body,og.body_len);
+ }
+}
+
+KviTheoraEncoder::~KviTheoraEncoder()
+{
+ // Call th_encode_free() to release all encoder memory.
+ th_encode_free(td);
+
+ // clear out state
+
+ ogg_stream_clear(&to);
+ th_comment_clear(&tc);
+
+ irct_encode_clear();
+}
+
+void KviTheoraEncoder::addVideoFrame(QRgb * rgb32, int)
+{
+ /*
+ * For each uncompressed frame:
+ * o Submit the uncompressed frame via th_encode_ycbcr_in()
+ * o Repeatedly call th_encode_packetout() to retrieve any video data packets that are ready.
+ */
+
+ if(!videoYuv)
+ videoYuv = new quint8[geometry.pic_w * geometry.pic_h * YUV444_BPP];
+
+ rgb32toyuv444(rgb32, videoYuv, geometry.pic_w, geometry.pic_h);
+// qDebug("addFrame p%p size%d yuv%p",rgb32,videoSize,videoYuv);
+
+ ogg_page videopage;
+
+ // is there a video page flushed? If not, fetch one if possible
+ videoflag=fetch_and_process_video(videoYuv,&videopage,&to,td,videoflag);
+
+ // no pages?
+ if(!videoflag) return;
+
+ th_granule_time(td,ogg_page_granulepos(&videopage));
+
+ // flush a video page
+ m_pStream->append(videopage.header,videopage.header_len);
+ m_pStream->append(videopage.body,videopage.body_len);
+ videoflag=0;
+}
+
+void KviTheoraEncoder::addTextFrame(unsigned char* textPkt, int textSize)
+{
+ ogg_page textpage;
+ ogg_packet op;
+
+ text_sofar++;
+
+ // is there an audio page flushed? If not, fetch one if possible
+ textflag=irct_encode_packetout((char *) textPkt, textSize, text_sofar,&op);
+ ogg_stream_packetin(&zo, &op);
+ ogg_stream_pageout(&zo, &textpage);
+
+ // no pages? Must be end of stream.
+// if(!textflag) return;
+
+ m_pStream->append(textpage.header,textpage.header_len);
+ m_pStream->append(textpage.body,textpage.body_len);
+ textflag=0;
+}
+
+int KviTheoraEncoder::fetch_and_process_video_packet(quint8 * videoYuv,th_enc_ctx *td,ogg_packet *op)
+{
+ th_ycbcr_buffer ycbcr;
+
+ if(frame_state==-1)
+ {
+ /* initialize the double frame buffer */
+ yuvframe[0]=(unsigned char *)malloc(y4m_dst_buf_sz);
+ yuvframe[1]=(unsigned char *)malloc(y4m_dst_buf_sz);
+ yuvframe[2]=(unsigned char *)malloc(y4m_aux_buf_sz);
+ frame_state=0;
+ }
+ /* read and process more video */
+
+ /* have two frame buffers full (if possible) before
+ proceeding. after first pass and until eos, one will
+ always be full when we get here */
+ for(;frame_state<2;)
+ {
+ /*Read the frame data that needs no conversion.*/
+ memcpy(yuvframe[frame_state],videoYuv,y4m_dst_buf_read_sz);
+ /*Read the frame data that does need conversion.*/
+ memcpy(yuvframe[2],videoYuv,y4m_aux_buf_read_sz);
+ frame_state++;
+ }
+ /* check to see if there are dupes to flush */
+ if(th_encode_packetout(td,frame_state<1,op)>0)return 1;
+ if(frame_state<1)
+ {
+ /* can't get here unless YUV4MPEG stream has no video */
+ qDebug("Video input contains no frames.");
+ return 0;
+ }
+ /* Theora is a one-frame-in,one-frame-out system; submit a frame
+ for compression and pull out the packet */
+
+ /*We submit the buffer to the library as if it were padded, but we do not
+ actually allocate space for the padding.
+ This is okay, because with the 1.0 API the library will never read data from the padded
+ region.*/
+ ycbcr[0].width=geometry.frame_w;
+ ycbcr[0].height=geometry.frame_h;
+ ycbcr[0].stride=geometry.pic_w;
+ ycbcr[0].data=yuvframe[0] - geometry.pic_x -geometry.pic_y * geometry.pic_w;
+
+ ycbcr[1].width=geometry.frame_w;
+ ycbcr[1].height=geometry.frame_h;
+ ycbcr[1].stride=geometry.pic_w;
+ ycbcr[1].data=yuvframe[0] + (geometry.pic_w * geometry.pic_h) - geometry.pic_x - geometry.pic_y * geometry.frame_w;
+
+ ycbcr[2].width=geometry.frame_w;
+ ycbcr[2].height=geometry.frame_h;
+ ycbcr[2].stride=geometry.pic_w;
+ ycbcr[2].data=ycbcr[1].data+(geometry.frame_w * geometry.frame_h);
+
+ th_encode_ycbcr_in(td,ycbcr);
+ {
+ unsigned char *temp=yuvframe[0];
+ yuvframe[0]=yuvframe[1];
+ yuvframe[1]=temp;
+ frame_state--;
+ }
+ /* if there was only one frame, it's the last in the stream */
+ return th_encode_packetout(td,frame_state<1,op);
+}
+
+int KviTheoraEncoder::fetch_and_process_video(quint8 * videoYuv,ogg_page *videopage,
+ogg_stream_state *to,th_enc_ctx *td,int videoflag)
+{
+ ogg_packet op;
+ int ret;
+ /* is there a video page flushed? If not, work until there is. */
+ while(!videoflag)
+ {
+ //qDebug("fetch_and_process_video loop");
+ if(ogg_stream_pageout(to,videopage)>0) return 1;
+ if(ogg_stream_eos(to)) return 0;
+ ret=fetch_and_process_video_packet(videoYuv,td,&op);
+ if(ret<=0)return 0;
+ ogg_stream_packetin(to,&op);
+ }
+ return videoflag;
+}
+
+int KviTheoraEncoder::ilog(unsigned _v)
+{
+ int ret;
+ for(ret=0;_v;ret++)_v>>=1;
+ return ret;
+}
+
+
+//------------------------------------------------------------------------------
+#define OC_CLAMP255(_x) ((unsigned char)((((_x)<0)-1)&((_x)|-((_x)>255))))
+
+KviTheoraDecoder::KviTheoraDecoder(KviDataBuffer * videoSignal,KviDataBuffer * textSignal)
+{
+ m_pVideoSignal=videoSignal;
+ m_pTextSignal=textSignal;
+ theora_p=0;
+ stateflag=0;
+ ts=NULL;
+ thda=false;
+ thtic=false;
+
+ irct_p=0;
+
+ /* single frame video buffering */
+ videobuf_ready=0;
+ videobuf_granulepos=-1;
+ videobuf_time=0;
+
+ /* start up Ogg stream synchronization layer */
+ ogg_sync_init(&oy);
+
+ /* init supporting Theora structures needed in header parsing */
+ th_comment_init(&tc);
+ th_info_init(&ti);
+
+ for(int i=0;i<256;i++)
+ {
+ lu_Y[i] = 2441889*(i- 16) + 1048576;
+ lu_R[i] = 3347111*(i-128);
+ lu_GV[i] = -1704917*(i-128);
+ lu_GU[i] = -821585*(i-128);
+ lu_B[i] = 4230442*(i-128);
+ }
+}
+
+KviTheoraDecoder::~KviTheoraDecoder()
+{
+ if(theora_p){
+ ogg_stream_clear(&to);
+ th_decode_free(td);
+ th_comment_clear(&tc);
+ th_info_clear(&ti);
+ }
+
+ if(irct_p)
+ {
+ ogg_stream_clear(&zo);
+ irct_encode_clear();
+ }
+
+ ogg_sync_clear(&oy);
+}
+
+void KviTheoraDecoder::addData(KviDataBuffer * stream)
+{
+// qDebug("adddata signal %p stream size %d",m_pVideoSignal, stream->size() );
+ if(stream->size()==0)return;
+ /* Ogg file open; parse the headers */
+
+ if(!stateflag)
+ {
+
+ char *buffer=ogg_sync_buffer(&oy,stream->size());
+ memcpy(buffer,stream->data(),stream->size());
+ ogg_sync_wrote(&oy,stream->size());
+ stream->clear();
+
+ while(ogg_sync_pageout(&oy,&og)>0)
+ {
+ ogg_stream_state test;
+
+ /* is this a mandated initial header? If not, stop parsing */
+ if(!ogg_page_bos(&og))
+ {
+ /* don't leak the page; get it into the appropriate stream */
+// qDebug("queue_page && return");
+ queue_page(&og);
+ stateflag=1;
+ return;
+ }
+
+ ogg_stream_init(&test,ogg_page_serialno(&og));
+ ogg_stream_pagein(&test,&og);
+ ogg_stream_packetout(&test,&op);
+
+
+ /* identify the codec: try theora */
+ if(!theora_p && th_decode_headerin(&ti,&tc,&ts,&op)>=0){
+ qDebug("is theora, ts=%p &ts=%p",ts, &ts);
+ /* it is theora */
+ memcpy(&to,&test,sizeof(test));
+ theora_p=1;
+ }else if(!irct_p && irct_decode_headerin(&op)>=0){
+ qDebug("is irct");
+ /* it is irct */
+ memcpy(&zo,&test,sizeof(test));
+ irct_p=1;
+ }else{
+// qDebug("is other");
+ /* whatever it is, we don't care about it */
+ ogg_stream_clear(&test);
+ }
+ }
+ /* fall through to non-bos page parsing */
+ }
+
+// qDebug("checkpoint 2, theora_p=%d ",theora_p);
+ /* we're expecting more header packets. */
+ if((theora_p && theora_p<3))
+ {
+ int ret;
+
+ /* look for further theora headers */
+ while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op)))
+ {
+ if(ret<0){
+ qDebug("Error parsing Theora stream headers; corrupt stream?");
+ return;
+ }
+ if(!th_decode_headerin(&ti,&tc,&ts,&op)){
+ qDebug("Error parsing Theora stream headers; corrupt stream?");
+ return;
+ }
+ theora_p++;
+ }
+
+ /* The header pages/packets will arrive before anything else we
+ care about, or the stream is not obeying spec */
+ if(ogg_sync_pageout(&oy,&og)>0)
+ {
+ qDebug("queue_page");
+ queue_page(&og); /* demux into the appropriate stream */
+ } else {
+ qDebug("TheoraDecoder: need more data!");
+ //need more data
+ return;
+ }
+ }
+
+// qDebug("checkpoint 3");
+ /* and now we have it all. initialize decoders */
+ if(theora_p && !thda)
+ {
+ thda=true;
+ td=th_decode_alloc(&ti,ts);
+ qDebug("Ogg logical stream %lx is Theora %dx%d %.02f fps",
+ to.serialno,ti.pic_width,ti.pic_height,
+ (double)ti.fps_numerator/ti.fps_denominator);
+
+ geometry.pic_w = ti.pic_width;
+ geometry.pic_h = ti.pic_height;
+ geometry.frame_w = ti.frame_width;
+ geometry.frame_h = ti.frame_height;
+ geometry.pic_x = ti.pic_x;
+ geometry.pic_y = ti.pic_y;
+
+ px_fmt=ti.pixel_fmt;
+ switch(ti.pixel_fmt)
+ {
+ case TH_PF_444: qDebug(" 4:4:4 video"); break;
+ default:
+ qDebug(" video (UNSUPPORTED Chroma sampling!)");
+ return;
+ break;
+ }
+ th_decode_ctl(td,TH_DECCTL_GET_PPLEVEL_MAX,&pp_level_max,sizeof(pp_level_max));
+ pp_level=pp_level_max;
+ th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level,sizeof(pp_level));
+ pp_inc=0;
+
+ int w = ((ti.pic_x + ti.pic_width + 1) &~ 1) - (ti.pic_x &~ 1);
+ int h = ((ti.pic_y + ti.pic_height + 1) &~ 1) - (ti.pic_y &~ 1);
+ RGBbuffer = (unsigned char *) calloc(sizeof(char), w * h * 4);
+ } else if(!thtic)
+ {
+ thtic=true;
+ /* tear down the partial theora setup */
+ th_info_clear(&ti);
+ th_comment_clear(&tc);
+
+ th_setup_free(ts);
+ }
+// stateflag=0; /* playback has not begun */
+
+ if(irct_p)
+ {
+ if(ogg_stream_packetout(&zo,&op)>0)
+ {
+ char * textPkt=0;
+ int textSize=0;
+ if(irct_decode_packetin(&textPkt, &textSize, &op)==0)
+ {
+ m_pTextSignal->append((unsigned char *)textPkt, textSize);
+ }
+ }
+ }
+
+ if(theora_p && !videobuf_ready)
+ {
+ if(stream->size()>0)
+ {
+ char *buffer=ogg_sync_buffer(&oy,stream->size());
+ memcpy(buffer,stream->data(),stream->size());
+ ogg_sync_wrote(&oy,stream->size());
+ stream->clear();
+
+ while(ogg_sync_pageout(&oy,&og)>0)
+ queue_page(&og);
+ }
+// qDebug("loop4 theora_p=%d videobuf_ready=%d",theora_p, videobuf_ready);
+ // theora is one in, one out...
+
+ if(ogg_stream_packetout(&to,&op)>0)
+ {
+ if(th_decode_packetin(td,&op,&videobuf_granulepos)==0)
+ videobuf_ready=1;
+ } else {
+// qDebug("ogg_stream_packetout <=0");
+ //need more data :)
+ return;
+ }
+ }
+
+ if(!videobuf_ready)return;
+
+
+ if(stateflag && videobuf_ready)
+ {
+ video_write();
+ videobuf_ready=0;
+ }
+
+ // if our buffers either don't exist or are ready to go,we can begin playback
+ if(!theora_p || videobuf_ready)stateflag=1;
+}
+
+/* helper: push a page into the appropriate steam */
+/* this can be done blindly; a stream won't accept a page
+ that doesn't belong to it */
+int KviTheoraDecoder::queue_page(ogg_page *page)
+{
+ if(theora_p)ogg_stream_pagein(&to,page);
+ if(irct_p)ogg_stream_pagein(&zo,page);
+ return 0;
+}
+
+void KviTheoraDecoder::video_write(void)
+{
+ th_ycbcr_buffer yuv;
+ int y_offset;
+ th_decode_ycbcr_out(td,yuv);
+
+ y_offset=(geometry.pic_x&~1)+yuv[0].stride*(geometry.pic_y&~1);
+
+ for(int i=0;i<geometry.pic_h;i++)
+ {
+ unsigned char *in_y = (unsigned char *)yuv[0].data+y_offset+yuv[0].stride*i;
+ unsigned char *in_u = (unsigned char *)yuv[1].data+y_offset+yuv[1].stride*i;
+ unsigned char *in_v = (unsigned char *)yuv[2].data+y_offset+yuv[2].stride*i;
+ unsigned char *out = RGBbuffer+(geometry.pic_w * i * ARGB32_BPP);
+ for (int j=0;j<geometry.pic_w;j++)
+ {
+ int r, g, b;
+ int y = lu_Y[in_y[j]];
+ b=(y + lu_B[in_u[j]])>>21;
+ out[j*4] = OC_CLAMP255(b);
+ g=(y + lu_GV[in_v[j]] + lu_GU[in_u[j]])>>21;
+ out[j*4+1] = OC_CLAMP255(g);
+ r=(y + lu_R[in_v[j]])>>21;
+ out[j*4+2] = OC_CLAMP255(r);
+ out[j*4+3] = 255; //alpha
+ }
+
+ }
+
+ int size = sizeof(char) * geometry.pic_w * geometry.pic_h * ARGB32_BPP;
+
+// qDebug("VIDEO WRITE size=%d",size);
+ m_pVideoSignal->clear();
+ m_pVideoSignal->append(RGBbuffer, size);
+}
+
+#endif // COMPILE_DISABLE_OGG_THEORA \ No newline at end of file