1
//=============================================================================
3
// File : kvi_oggtheora.cpp
4
// Creation date : Sat Nov 21 2009 22:53:21 CEST by Fabio Bas
6
// This file is part of the KVirc irc client distribution
7
// Copyright (C) 2009 Fabio Bas (ctrlaltca at libero dot it)
9
// This program is FREE software. You can redistribute it and/or
10
// modify it under the terms of the GNU General Public License
11
// as published by the Free Software Foundation; either version 2
12
// of the License, or (at your opinion) any later version.
14
// This program is distributed in the HOPE that it will be USEFUL,
15
// but WITHOUT ANY WARRANTY; without even the implied warranty of
16
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
// See the GNU General Public License for more details.
19
// You should have received a copy of the GNU General Public License
20
// along with this program. If not, write to the Free Software Foundation,
21
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23
// Based on the example encoder/player from Xiph.Org Foundation:
24
//=============================================================================
26
// function: example encoder application; makes an Ogg Theora/Vorbis
27
// file from YUV4MPEG2 and WAV input
28
// last mod: $Id: encoder_example.c 16517 2009-08-25 19:48:57Z giles $
30
//=============================================================================
32
// function: example SDL player application; plays Ogg Theora files (with
33
// optional Vorbis audio second stream)
34
// last mod: $Id: player_example.c 16628 2009-10-10 05:50:52Z gmaxwell $
36
//=============================================================================
38
#ifndef COMPILE_DISABLE_OGG_THEORA
46
#include "kvi_oggtheora.h"
47
#include "kvi_oggirct.h"
49
#include "kvi_databuffer.h"
51
using namespace KviOggIrcText;
53
static void rgb32toyuv444(QRgb * rgbPt, unsigned char *yuvPt, int w, int h)
55
for(int i = 0; i < h; i++) {
56
for (int j = 0; j < w; j++) {
57
yuvPt[i * w + j] = (unsigned char) (( 66 * qRed(*rgbPt) + 129 * qGreen(*rgbPt) + 25 * qBlue(*rgbPt) + 128) >> 8) + 16; // y
58
yuvPt[(i + h) * w + j] = (unsigned char) ((-38 * qRed(*rgbPt) - 74 * qGreen(*rgbPt) + 112 * qBlue(*rgbPt) + 128) >> 8) + 128; // u
59
yuvPt[(i + 2 * h) * w + j] = (unsigned char) ((112 * qRed(*rgbPt) - 94 * qGreen(*rgbPt) - 18 * qBlue(*rgbPt) + 128) >> 8) + 128; // v
60
//this moves the pointer forward 4 bytes (ARGB)
67
KviTheoraEncoder::KviTheoraEncoder(KviDataBuffer * stream, int iWidth, int iHeight, int iFpsN, int iFpsD, int iParN, int iParD)
69
//used to check functions results
80
geometry.pic_w=iWidth;
81
geometry.pic_h=iHeight;
83
int video_fps_n=iFpsN;
84
int video_fps_d=iFpsD;
86
int video_par_n=iParN;
87
int video_par_d=iParD;
92
ogg_uint32_t keyframe_frequency=64;
94
y4m_dst_buf_read_sz = geometry.pic_w * geometry.pic_h * 3;
95
y4m_aux_buf_sz = y4m_aux_buf_read_sz=0;
98
y4m_dst_buf_read_sz = geometry.pic_w * geometry.pic_h * 3;
99
y4m_aux_buf_sz = y4m_aux_buf_read_sz=0;
100
// The size of the final frame buffers is always computed from the destination chroma decimation type.
101
y4m_dst_buf_sz = geometry.pic_w * geometry.pic_h * 3;
103
// Set up Ogg output stream
105
ogg_stream_init(&to,rand());
107
ogg_stream_init(&zo,rand());
108
ret = irct_encode_init();
111
qDebug("error initializing irctext");
116
// Set up Theora encoder
117
// Theora has a divisible-by-sixteen restriction for the encoded frame size
118
// scale the picture size up to the nearest /16 and calculate offsets
119
geometry.frame_w = (geometry.pic_w + 15) &~ 0xF;
120
geometry.frame_h = (geometry.pic_h + 15) &~ 0xF;
121
// Force the offsets to be even so that chroma samples line up like we expect.
122
geometry.pic_x = (geometry.frame_w - geometry.pic_w) >> 1 &~ 1;
123
geometry.pic_y = (geometry.frame_h - geometry.pic_h) >> 1 &~ 1;
125
// Fill in a th_info structure with details on the format of the video you wish to encode.
127
ti.frame_width=geometry.frame_w;
128
ti.frame_height=geometry.frame_h;
129
ti.pic_width=geometry.pic_w;
130
ti.pic_height=geometry.pic_h;
131
ti.pic_x=geometry.pic_x;
132
ti.pic_y=geometry.pic_y;
133
ti.fps_numerator=video_fps_n;
134
ti.fps_denominator=video_fps_d;
135
ti.aspect_numerator=video_par_n;
136
ti.aspect_denominator=video_par_d;
137
ti.colorspace=TH_CS_UNSPECIFIED;
141
ti.keyframe_granule_shift=ilog(keyframe_frequency-1);
142
ti.pixel_fmt=TH_PF_444;
143
// Allocate a th_enc_ctx handle with th_encode_alloc().
144
td=th_encode_alloc(&ti);
146
qDebug("Could not th_encode_alloc().");
151
// Perform any additional encoder configuration required with th_encode_ctl().
152
// setting just the granule shift only allows power-of-two keyframe spacing. Set the actual requested spacing.
153
ret=th_encode_ctl(td,TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,&keyframe_frequency,sizeof(keyframe_frequency-1));
155
qDebug("Could not set keyframe interval to %d.",(int)keyframe_frequency);
157
// write the bitstream header packets with proper page interleave
158
th_comment_init(&tc);
160
// Repeatedly call th_encode_flushheader() to retrieve all the header packets.
161
// first packet will get its own page automatically
162
if(th_encode_flushheader(td,&tc,&op)<=0){
163
qDebug("Internal Theora library error.");
167
ogg_stream_packetin(&to,&op);
168
if(ogg_stream_pageout(&to,&og)!=1){
169
qDebug("Internal Ogg library error.");
173
m_pStream->append(og.header,og.header_len);
174
m_pStream->append(og.body,og.body_len);
176
// create the remaining theora headers
178
ret=th_encode_flushheader(td,&tc,&op);
180
qDebug("Internal Theora library error.");
184
ogg_stream_packetin(&to,&op);
189
irct_encode_headerout(&header);
190
ogg_stream_packetin(&zo,&header); // automatically placed in its own page
191
if(ogg_stream_pageout(&zo,&og)!=1){
192
qDebug("Internal Ogg library error (irct).");
195
m_pStream->append(og.header,og.header_len);
196
m_pStream->append(og.body,og.body_len);
198
// Flush the rest of our headers. This ensures the actual data in each stream
199
// will start on a new page, as per spec.
202
int result = ogg_stream_flush(&to,&og);
205
qDebug("Internal Ogg library error.");
209
m_pStream->append(og.header,og.header_len);
210
m_pStream->append(og.body,og.body_len);
214
KviTheoraEncoder::~KviTheoraEncoder()
216
// Call th_encode_free() to release all encoder memory.
221
ogg_stream_clear(&to);
222
th_comment_clear(&tc);
227
void KviTheoraEncoder::addVideoFrame(QRgb * rgb32, int)
230
* For each uncompressed frame:
231
* o Submit the uncompressed frame via th_encode_ycbcr_in()
232
* o Repeatedly call th_encode_packetout() to retrieve any video data packets that are ready.
236
videoYuv = new quint8[geometry.pic_w * geometry.pic_h * YUV444_BPP];
238
rgb32toyuv444(rgb32, videoYuv, geometry.pic_w, geometry.pic_h);
239
// qDebug("addFrame p%p size%d yuv%p",rgb32,videoSize,videoYuv);
243
// is there a video page flushed? If not, fetch one if possible
244
videoflag=fetch_and_process_video(videoYuv,&videopage,&to,td,videoflag);
247
if(!videoflag) return;
249
th_granule_time(td,ogg_page_granulepos(&videopage));
251
// flush a video page
252
m_pStream->append(videopage.header,videopage.header_len);
253
m_pStream->append(videopage.body,videopage.body_len);
257
void KviTheoraEncoder::addTextFrame(unsigned char* textPkt, int textSize)
264
// is there an audio page flushed? If not, fetch one if possible
265
textflag=irct_encode_packetout((char *) textPkt, textSize, text_sofar,&op);
266
ogg_stream_packetin(&zo, &op);
267
ogg_stream_pageout(&zo, &textpage);
269
// no pages? Must be end of stream.
270
// if(!textflag) return;
272
m_pStream->append(textpage.header,textpage.header_len);
273
m_pStream->append(textpage.body,textpage.body_len);
277
int KviTheoraEncoder::fetch_and_process_video_packet(quint8 * videoYuv,th_enc_ctx *td,ogg_packet *op)
279
th_ycbcr_buffer ycbcr;
283
/* initialize the double frame buffer */
284
yuvframe[0]=(unsigned char *)malloc(y4m_dst_buf_sz);
285
yuvframe[1]=(unsigned char *)malloc(y4m_dst_buf_sz);
286
yuvframe[2]=(unsigned char *)malloc(y4m_aux_buf_sz);
289
/* read and process more video */
291
/* have two frame buffers full (if possible) before
292
proceeding. after first pass and until eos, one will
293
always be full when we get here */
296
/*Read the frame data that needs no conversion.*/
297
memcpy(yuvframe[frame_state],videoYuv,y4m_dst_buf_read_sz);
298
/*Read the frame data that does need conversion.*/
299
memcpy(yuvframe[2],videoYuv,y4m_aux_buf_read_sz);
302
/* check to see if there are dupes to flush */
303
if(th_encode_packetout(td,frame_state<1,op)>0)return 1;
306
/* can't get here unless YUV4MPEG stream has no video */
307
qDebug("Video input contains no frames.");
310
/* Theora is a one-frame-in,one-frame-out system; submit a frame
311
for compression and pull out the packet */
313
/*We submit the buffer to the library as if it were padded, but we do not
314
actually allocate space for the padding.
315
This is okay, because with the 1.0 API the library will never read data from the padded
317
ycbcr[0].width=geometry.frame_w;
318
ycbcr[0].height=geometry.frame_h;
319
ycbcr[0].stride=geometry.pic_w;
320
ycbcr[0].data=yuvframe[0] - geometry.pic_x -geometry.pic_y * geometry.pic_w;
322
ycbcr[1].width=geometry.frame_w;
323
ycbcr[1].height=geometry.frame_h;
324
ycbcr[1].stride=geometry.pic_w;
325
ycbcr[1].data=yuvframe[0] + (geometry.pic_w * geometry.pic_h) - geometry.pic_x - geometry.pic_y * geometry.frame_w;
327
ycbcr[2].width=geometry.frame_w;
328
ycbcr[2].height=geometry.frame_h;
329
ycbcr[2].stride=geometry.pic_w;
330
ycbcr[2].data=ycbcr[1].data+(geometry.frame_w * geometry.frame_h);
332
th_encode_ycbcr_in(td,ycbcr);
334
unsigned char *temp=yuvframe[0];
335
yuvframe[0]=yuvframe[1];
339
/* if there was only one frame, it's the last in the stream */
340
return th_encode_packetout(td,frame_state<1,op);
343
int KviTheoraEncoder::fetch_and_process_video(quint8 * videoYuv,ogg_page *videopage,
344
ogg_stream_state *to,th_enc_ctx *td,int videoflag)
348
/* is there a video page flushed? If not, work until there is. */
351
//qDebug("fetch_and_process_video loop");
352
if(ogg_stream_pageout(to,videopage)>0) return 1;
353
if(ogg_stream_eos(to)) return 0;
354
ret=fetch_and_process_video_packet(videoYuv,td,&op);
356
ogg_stream_packetin(to,&op);
361
int KviTheoraEncoder::ilog(unsigned _v)
364
for(ret=0;_v;ret++)_v>>=1;
369
//------------------------------------------------------------------------------
370
#define OC_CLAMP255(_x) ((unsigned char)((((_x)<0)-1)&((_x)|-((_x)>255))))
372
KviTheoraDecoder::KviTheoraDecoder(KviDataBuffer * videoSignal,KviDataBuffer * textSignal)
374
m_pVideoSignal=videoSignal;
375
m_pTextSignal=textSignal;
384
/* single frame video buffering */
386
videobuf_granulepos=-1;
389
/* start up Ogg stream synchronization layer */
392
/* init supporting Theora structures needed in header parsing */
393
th_comment_init(&tc);
396
for(int i=0;i<256;i++)
398
lu_Y[i] = 2441889*(i- 16) + 1048576;
399
lu_R[i] = 3347111*(i-128);
400
lu_GV[i] = -1704917*(i-128);
401
lu_GU[i] = -821585*(i-128);
402
lu_B[i] = 4230442*(i-128);
406
KviTheoraDecoder::~KviTheoraDecoder()
409
ogg_stream_clear(&to);
411
th_comment_clear(&tc);
417
ogg_stream_clear(&zo);
424
void KviTheoraDecoder::addData(KviDataBuffer * stream)
426
// qDebug("adddata signal %p stream size %d",m_pVideoSignal, stream->size() );
427
if(stream->size()==0)return;
428
/* Ogg file open; parse the headers */
433
char *buffer=ogg_sync_buffer(&oy,stream->size());
434
memcpy(buffer,stream->data(),stream->size());
435
ogg_sync_wrote(&oy,stream->size());
438
while(ogg_sync_pageout(&oy,&og)>0)
440
ogg_stream_state test;
442
/* is this a mandated initial header? If not, stop parsing */
443
if(!ogg_page_bos(&og))
445
/* don't leak the page; get it into the appropriate stream */
446
// qDebug("queue_page && return");
452
ogg_stream_init(&test,ogg_page_serialno(&og));
453
ogg_stream_pagein(&test,&og);
454
ogg_stream_packetout(&test,&op);
457
/* identify the codec: try theora */
458
if(!theora_p && th_decode_headerin(&ti,&tc,&ts,&op)>=0){
459
qDebug("is theora, ts=%p &ts=%p",ts, &ts);
461
memcpy(&to,&test,sizeof(test));
463
}else if(!irct_p && irct_decode_headerin(&op)>=0){
466
memcpy(&zo,&test,sizeof(test));
469
// qDebug("is other");
470
/* whatever it is, we don't care about it */
471
ogg_stream_clear(&test);
474
/* fall through to non-bos page parsing */
477
// qDebug("checkpoint 2, theora_p=%d ",theora_p);
478
/* we're expecting more header packets. */
479
if((theora_p && theora_p<3))
483
/* look for further theora headers */
484
while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op)))
487
qDebug("Error parsing Theora stream headers; corrupt stream?");
490
if(!th_decode_headerin(&ti,&tc,&ts,&op)){
491
qDebug("Error parsing Theora stream headers; corrupt stream?");
497
/* The header pages/packets will arrive before anything else we
498
care about, or the stream is not obeying spec */
499
if(ogg_sync_pageout(&oy,&og)>0)
501
qDebug("queue_page");
502
queue_page(&og); /* demux into the appropriate stream */
504
qDebug("TheoraDecoder: need more data!");
510
// qDebug("checkpoint 3");
511
/* and now we have it all. initialize decoders */
512
if(theora_p && !thda)
515
td=th_decode_alloc(&ti,ts);
516
qDebug("Ogg logical stream %lx is Theora %dx%d %.02f fps",
517
to.serialno,ti.pic_width,ti.pic_height,
518
(double)ti.fps_numerator/ti.fps_denominator);
520
geometry.pic_w = ti.pic_width;
521
geometry.pic_h = ti.pic_height;
522
geometry.frame_w = ti.frame_width;
523
geometry.frame_h = ti.frame_height;
524
geometry.pic_x = ti.pic_x;
525
geometry.pic_y = ti.pic_y;
530
case TH_PF_444: qDebug(" 4:4:4 video"); break;
532
qDebug(" video (UNSUPPORTED Chroma sampling!)");
536
th_decode_ctl(td,TH_DECCTL_GET_PPLEVEL_MAX,&pp_level_max,sizeof(pp_level_max));
537
pp_level=pp_level_max;
538
th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level,sizeof(pp_level));
541
int w = ((ti.pic_x + ti.pic_width + 1) &~ 1) - (ti.pic_x &~ 1);
542
int h = ((ti.pic_y + ti.pic_height + 1) &~ 1) - (ti.pic_y &~ 1);
543
RGBbuffer = (unsigned char *) calloc(sizeof(char), w * h * 4);
547
/* tear down the partial theora setup */
549
th_comment_clear(&tc);
553
// stateflag=0; /* playback has not begun */
557
if(ogg_stream_packetout(&zo,&op)>0)
561
if(irct_decode_packetin(&textPkt, &textSize, &op)==0)
563
m_pTextSignal->append((unsigned char *)textPkt, textSize);
568
if(theora_p && !videobuf_ready)
572
char *buffer=ogg_sync_buffer(&oy,stream->size());
573
memcpy(buffer,stream->data(),stream->size());
574
ogg_sync_wrote(&oy,stream->size());
577
while(ogg_sync_pageout(&oy,&og)>0)
580
// qDebug("loop4 theora_p=%d videobuf_ready=%d",theora_p, videobuf_ready);
581
// theora is one in, one out...
583
if(ogg_stream_packetout(&to,&op)>0)
585
if(th_decode_packetin(td,&op,&videobuf_granulepos)==0)
588
// qDebug("ogg_stream_packetout <=0");
594
if(!videobuf_ready)return;
597
if(stateflag && videobuf_ready)
603
// if our buffers either don't exist or are ready to go,we can begin playback
604
if(!theora_p || videobuf_ready)stateflag=1;
607
/* helper: push a page into the appropriate steam */
608
/* this can be done blindly; a stream won't accept a page
609
that doesn't belong to it */
610
int KviTheoraDecoder::queue_page(ogg_page *page)
612
if(theora_p)ogg_stream_pagein(&to,page);
613
if(irct_p)ogg_stream_pagein(&zo,page);
617
void KviTheoraDecoder::video_write(void)
621
th_decode_ycbcr_out(td,yuv);
623
y_offset=(geometry.pic_x&~1)+yuv[0].stride*(geometry.pic_y&~1);
625
for(int i=0;i<geometry.pic_h;i++)
627
unsigned char *in_y = (unsigned char *)yuv[0].data+y_offset+yuv[0].stride*i;
628
unsigned char *in_u = (unsigned char *)yuv[1].data+y_offset+yuv[1].stride*i;
629
unsigned char *in_v = (unsigned char *)yuv[2].data+y_offset+yuv[2].stride*i;
630
unsigned char *out = RGBbuffer+(geometry.pic_w * i * ARGB32_BPP);
631
for (int j=0;j<geometry.pic_w;j++)
634
int y = lu_Y[in_y[j]];
635
b=(y + lu_B[in_u[j]])>>21;
636
out[j*4] = OC_CLAMP255(b);
637
g=(y + lu_GV[in_v[j]] + lu_GU[in_u[j]])>>21;
638
out[j*4+1] = OC_CLAMP255(g);
639
r=(y + lu_R[in_v[j]])>>21;
640
out[j*4+2] = OC_CLAMP255(r);
641
out[j*4+3] = 255; //alpha
646
int size = sizeof(char) * geometry.pic_w * geometry.pic_h * ARGB32_BPP;
648
// qDebug("VIDEO WRITE size=%d",size);
649
m_pVideoSignal->clear();
650
m_pVideoSignal->append(RGBbuffer, size);
653
#endif // COMPILE_DISABLE_OGG_THEORA
b'\\ No newline at end of file'