1 /*
2  * THEORA Plugin codec for OpenH323/OPAL
3  *
4  * Copyright (C) Matthias Schneider, All Rights Reserved
5  *
6  * This code is based on the file h261codec.cxx from the OPAL project released
7  * under the MPL 1.0 license which contains the following:
8  *
9  * Copyright (c) 1998-2000 Equivalence Pty. Ltd.
10  *
11  * The contents of this file are subject to the Mozilla Public License
12  * Version 1.0 (the "License"); you may not use this file except in
13  * compliance with the License. You may obtain a copy of the License at
14  * http://www.mozilla.org/MPL/
15  *
16  * Software distributed under the License is distributed on an "AS IS"
17  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
18  * the License for the specific language governing rights and limitations
19  * under the License.
20  *
21  * The Original Code is Open H323 Library.
22  *
23  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24  *
25  * Contributor(s): Matthias Schneider (ma30002000@yahoo.de)
26  *                 Michele Piccini (michele@piccini.com)
27  *                 Derek Smithies (derek@indranet.co.nz)
28  *
29  *
30  */
31 
32 /*
33   Notes
34   -----
35 
36  */
37 
38 #ifndef PLUGIN_CODEC_DLL_EXPORTS
39 #include "plugin-config.h"
40 #endif
41 
42 #include <codec/opalplugin.hpp>
43 
44 #include "theora_plugin.h"
45 
46 #include "rtpframe.h"
47 
48 #include <stdlib.h>
49 #if defined(_WIN32) || defined(_WIN32_WCE)
50   #include <malloc.h>
51   #define STRCMPI  _strcmpi
52 #else
53   #include <semaphore.h>
54   #define STRCMPI  strcasecmp
55 #endif
56 #include <string.h>
57 #include <stdio.h>
58 
theoraEncoderContext()59 theoraEncoderContext::theoraEncoderContext()
60 {
61   ogg_packet headerPacket, tablePacket;
62   _frameCounter=0;
63 
64   _txTheoraFrame = new theoraFrame();
65   _txTheoraFrame->SetMaxPayloadSize(THEORA_PAYLOAD_SIZE);
66 
67   theora_info_init( &_theoraInfo );
68   _theoraInfo.frame_width        = 352;  // Must be multiple of 16
69   _theoraInfo.frame_height       = 288; // Must be multiple of 16
70   _theoraInfo.width              = _theoraInfo.frame_width;
71   _theoraInfo.height             = _theoraInfo.frame_height;
72   _theoraInfo.offset_x           = 0;
73   _theoraInfo.offset_y           = 0;
74   _theoraInfo.fps_numerator      = THEORA_FRAME_RATE;
75   _theoraInfo.fps_denominator    = 1;
76   _theoraInfo.aspect_numerator   = _theoraInfo.width;  // Aspect =  width/height
77   _theoraInfo.aspect_denominator = _theoraInfo.height; //
78   _theoraInfo.colorspace         = OC_CS_UNSPECIFIED;
79   _theoraInfo.target_bitrate     = THEORA_BITRATE * 2 / 3; // Anywhere between 45kbps and 2000kbps
80   _theoraInfo.quality            = 16;
81   _theoraInfo.dropframes_p                 = 0;
82   _theoraInfo.quick_p                      = 1;
83   _theoraInfo.keyframe_auto_p              = 1;
84   _theoraInfo.keyframe_frequency = THEORA_KEY_FRAME_INTERVAL;
85   _theoraInfo.keyframe_frequency_force     = _theoraInfo.keyframe_frequency;
86   _theoraInfo.keyframe_data_target_bitrate = THEORA_BITRATE;
87   _theoraInfo.keyframe_auto_threshold      = 80;
88   _theoraInfo.keyframe_mindistance         = 8;
89   _theoraInfo.noise_sensitivity            = 1;
90 
91   theora_encode_init( &_theoraState, &_theoraInfo );
92 
93   theora_encode_header( &_theoraState, &headerPacket );
94   _txTheoraFrame->SetFromHeaderConfig(&headerPacket);
95 
96   theora_encode_tables( &_theoraState, &tablePacket );
97   _txTheoraFrame->SetFromTableConfig(&tablePacket);
98 }
99 
~theoraEncoderContext()100 theoraEncoderContext::~theoraEncoderContext()
101 {
102   theora_clear( &_theoraState );
103   theora_info_clear (&_theoraInfo);
104   if (_txTheoraFrame) delete _txTheoraFrame;
105 }
106 
SetTargetBitrate(unsigned rate)107 void theoraEncoderContext::SetTargetBitrate(unsigned rate)
108 {
109   _theoraInfo.target_bitrate     = rate * 2 / 3; // Anywhere between 45kbps and 2000kbps}
110   _theoraInfo.keyframe_data_target_bitrate = rate;
111 }
112 
SetFrameRate(unsigned rate)113 void theoraEncoderContext::SetFrameRate(unsigned rate)
114 {
115   _theoraInfo.fps_numerator      = (int)((rate + .5) * 1000); // ???
116   _theoraInfo.fps_denominator    = 1000;
117 }
118 
SetFrameWidth(unsigned width)119 void theoraEncoderContext::SetFrameWidth(unsigned width)
120 {
121   _theoraInfo.frame_width        = width;  // Must be multiple of 16
122   _theoraInfo.width              = _theoraInfo.frame_width;
123 }
124 
SetFrameHeight(unsigned height)125 void theoraEncoderContext::SetFrameHeight(unsigned height)
126 {
127   _theoraInfo.frame_height       = height; // Must be multiple of 16
128   _theoraInfo.height             = _theoraInfo.frame_height;
129 }
130 
ApplyOptions()131 void theoraEncoderContext::ApplyOptions()
132 {
133   ogg_packet headerPacket, tablePacket;
134 
135   theora_clear( &_theoraState );
136   theora_encode_init( &_theoraState, &_theoraInfo );
137 
138   theora_encode_header( &_theoraState, &headerPacket );
139   _txTheoraFrame->SetFromHeaderConfig(&headerPacket);
140 
141   theora_encode_tables( &_theoraState, &tablePacket );
142   _txTheoraFrame->SetFromTableConfig(&tablePacket);
143 }
Lock()144 void theoraEncoderContext::Lock()
145 {
146   _mutex.Wait();
147 }
148 
Unlock()149 void theoraEncoderContext::Unlock()
150 {
151   _mutex.Signal();
152 }
153 
EncodeFrames(const u_char * src,unsigned & srcLen,u_char * dst,unsigned & dstLen,unsigned int & flags)154 int theoraEncoderContext::EncodeFrames(const u_char * src, unsigned & srcLen, u_char * dst, unsigned & dstLen, unsigned int & flags)
155 {
156   WaitAndSignal m(_mutex);
157   int ret;
158   yuv_buffer yuv;
159   ogg_packet framePacket;
160 
161   // create RTP frame from source buffer
162   RTPFrame srcRTP(src, srcLen);
163 
164   // create RTP frame from destination buffer
165   RTPFrame dstRTP(dst, dstLen);
166 
167   dstLen = 0;
168 
169   // from here, we are encoding a new frame
170   if (!_txTheoraFrame)
171   {
172     return 0;
173   }
174 
175   // if there are RTP packets to return, return them
176   if  (_txTheoraFrame->HasRTPFrames())
177   {
178     _txTheoraFrame->GetRTPFrame(dstRTP, flags);
179     dstLen = dstRTP.GetFrameLen();
180     return 1;
181   }
182 
183   if (srcRTP.GetPayloadSize() < sizeof(frameHeader))
184   {
185    PTRACE(1, "THEORA", "Encoder\tVideo grab too small, Close down video transmission thread");
186    return 0;
187   }
188 
189   frameHeader * header = (frameHeader *)srcRTP.GetPayloadPtr();
190   if (header->x != 0 || header->y != 0)
191   {
192     PTRACE(1, "THEORA", "Encoder\tVideo grab of partial frame unsupported, Close down video transmission thread");
193     return 0;
194   }
195 
196   // do a validation of size
197   // if the incoming data has changed size, tell the encoder
198   if (_theoraInfo.frame_width != header->width || _theoraInfo.frame_height != header->height)
199   {
200     _theoraInfo.frame_width        = header->width;  // Must be multiple of 16
201     _theoraInfo.frame_height       = header->height; // Must be multiple of 16
202     _theoraInfo.width              = _theoraInfo.frame_width;
203     _theoraInfo.height             = _theoraInfo.frame_height;
204     _theoraInfo.aspect_numerator   = _theoraInfo.width;  // Aspect =  width/height
205     _theoraInfo.aspect_denominator = _theoraInfo.height; //
206     ApplyOptions();
207   }
208 
209   // Prepare the frame to be encoded
210   yuv.y_width   = header->width;
211   yuv.y_height  = _theoraInfo.height;
212   yuv.uv_width  = (int) (header->width / 2);
213   yuv.uv_height = (int) (_theoraInfo.height / 2);
214   yuv.y_stride  = header->width;
215   yuv.uv_stride = (int) (header->width /2);
216   yuv.y         = (unsigned char *)(((unsigned char *)header) + sizeof(frameHeader));
217   yuv.u         = (unsigned char *)((((unsigned char *)header) + sizeof(frameHeader))
218                            + (int)(yuv.y_stride*header->height));
219   yuv.v         = (unsigned char *)(yuv.u + (int)(yuv.uv_stride *header->height/2));
220 
221   ret = theora_encode_YUVin( &_theoraState, &yuv );
222   if (ret != 0) {
223     if (ret == -1) {
224       PTRACE(1, "THEORA", "Encoder\tEncoding failed: The size of the given frame differs from those previously input (should not happen)");
225     } else {
226       PTRACE(1, "THEORA", "Encoder\tEncoding failed: " << theoraErrorMessage(ret));
227     }
228     return 0;
229   }
230 
231   ret = theora_encode_packetout( &_theoraState, 0 /* not last Packet */, &framePacket );
232   switch (ret) {
233     case  0: PTRACE(1, "THEORA", "Encoder\tEncoding failed (packet): No internal storage exists OR no packet is ready"); return 0; break;
234     case -1: PTRACE(1, "THEORA", "Encoder\tEncoding failed (packet): The encoding process has completed but something is not ready yet"); return 0; break;
235     case  1: PTRACE(4, "THEORA", "Encoder\tSuccessfully encoded OGG packet of " << framePacket.bytes << " bytes"); break;
236     default: PTRACE(1, "THEORA", "Encoder\tEncoding failed (packet): " << theoraErrorMessage(ret)); return 0; break;
237   }
238 
239   _txTheoraFrame->SetFromFrame(&framePacket);
240   _txTheoraFrame->SetIsIFrame(theora_packet_iskeyframe(&framePacket));
241   _txTheoraFrame->SetTimestamp(srcRTP.GetTimestamp());
242   _frameCounter++;
243 
244   if (_txTheoraFrame->HasRTPFrames())
245   {
246     _txTheoraFrame->GetRTPFrame(dstRTP, flags);
247     dstLen = dstRTP.GetFrameLen();
248     return 1;
249   }
250 
251   return 0;
252 }
253 
SetMaxRTPFrameSize(unsigned size)254 void theoraEncoderContext::SetMaxRTPFrameSize (unsigned size)
255 {
256   _txTheoraFrame->SetMaxPayloadSize(size);
257 }
258 
SetMaxKeyFramePeriod(unsigned period)259 void theoraEncoderContext::SetMaxKeyFramePeriod (unsigned period)
260 {
261   _theoraInfo.keyframe_frequency = period;
262 }
263 
theoraDecoderContext()264 theoraDecoderContext::theoraDecoderContext()
265 {
266   _frameCounter = 0;
267   _gotHeader = false;
268   _gotTable  = false;
269   _gotIFrame = false;
270   _gotAGoodFrame = true;
271 
272   _rxTheoraFrame = new theoraFrame();
273   theora_info_init( &_theoraInfo );
274 }
275 
~theoraDecoderContext()276 theoraDecoderContext::~theoraDecoderContext()
277 {
278   if (_gotHeader && _gotTable) theora_clear( &_theoraState );
279   theora_info_clear( &_theoraInfo );
280   if (_rxTheoraFrame) delete _rxTheoraFrame;
281 }
282 
DecodeFrames(const u_char * src,unsigned & srcLen,u_char * dst,unsigned & dstLen,unsigned int & flags)283 int theoraDecoderContext::DecodeFrames(const u_char * src, unsigned & srcLen, u_char * dst, unsigned & dstLen, unsigned int & flags)
284 {
285   WaitAndSignal m(_mutex);
286 
287   // create RTP frame from source buffer
288   RTPFrame srcRTP(src, srcLen);
289 
290   // create RTP frame from destination buffer
291   RTPFrame dstRTP(dst, dstLen, 0);
292   dstLen = 0;
293 
294   if (!_rxTheoraFrame->SetFromRTPFrame(srcRTP, flags)) {
295     _rxTheoraFrame->BeginNewFrame();
296     flags = (_gotAGoodFrame ? requestIFrame : 0);
297     _gotAGoodFrame = false;
298     return 1;
299   };
300 
301 // linphone does not send markers so for now we ignore them (should be fixed)
302 //if (srcRTP.GetMarker()==0)
303 //  return 1;
304 
305   if (!_rxTheoraFrame->HasOggPackets())
306     return 1;
307 
308   yuv_buffer yuv;
309   ogg_packet oggPacket;
310   theora_comment theoraComment;
311   bool gotFrame = false;
312   int ret;
313 
314   while (_rxTheoraFrame->HasOggPackets()) {
315     _rxTheoraFrame->GetOggPacket (&oggPacket);
316     if (theora_packet_isheader(&oggPacket)) {
317 
318       PTRACE(4, "THEORA", "Decoder\tGot OGG header packet with size " << oggPacket.bytes);
319 
320       // In case we receive new header packets when the stream is already established
321       // we have to wait to get both table and header packet until reopening the decoder
322       if (_gotHeader && _gotTable) {
323         PTRACE(4, "THEORA", "Decoder\tGot OGG header packet after stream was established");
324         theora_clear( &_theoraState );
325         theora_info_clear( &_theoraInfo );
326         theora_info_init( &_theoraInfo );
327         _gotHeader = false;
328         _gotTable = false;
329         _gotIFrame = false;
330       }
331 
332       theora_comment_init(&theoraComment);
333       theoraComment.vendor = (char*) "theora";
334       ret = theora_decode_header( &_theoraInfo, &theoraComment, &oggPacket );
335       if (ret != 0) {
336         PTRACE(1, "THEORA", "Decoder\tDecoding failed (header packet): " << theoraErrorMessage(ret));
337         flags = (_gotAGoodFrame ? requestIFrame : 0);
338         _gotAGoodFrame = false;
339         return 1;
340       }
341 
342       if (oggPacket.bytes == THEORA_HEADER_PACKET_SIZE)
343         _gotHeader = true;
344        else
345         _gotTable = true;
346 
347       if (_gotHeader && _gotTable) theora_decode_init( &_theoraState, &_theoraInfo );
348       if (!_rxTheoraFrame->HasOggPackets())
349         return 1;
350     }
351     else {
352 
353       if (_gotHeader && _gotTable) {
354 
355         if (theora_packet_iskeyframe(&oggPacket)) {
356 
357           PTRACE(4, "THEORA", "Decoder\tGot OGG keyframe data packet with size " << oggPacket.bytes);
358           ret = theora_decode_packetin( &_theoraState, &oggPacket );
359           if (ret != 0) {
360             PTRACE(1, "THEORA", "Decoder\tDecoding failed (packet): " << theoraErrorMessage(ret));
361             flags = (_gotAGoodFrame ? requestIFrame : 0);
362             _gotAGoodFrame = false;
363             return 1;
364           }
365           theora_decode_YUVout( &_theoraState, &yuv);
366           _gotIFrame = true;
367           gotFrame = true;
368         }
369         else {
370 
371           if  (_gotIFrame) {
372 
373             PTRACE(4, "THEORA", "Decoder\tGot OGG non-keyframe data packet with size " << oggPacket.bytes);
374             ret = theora_decode_packetin( &_theoraState, &oggPacket );
375             if (ret != 0) {
376               PTRACE(1, "THEORA", "Decoder\tDecoding failed (packet): " << theoraErrorMessage(ret));
377               flags = (_gotAGoodFrame ? requestIFrame : 0);
378               _gotAGoodFrame = false;
379               return 1;
380             }
381             theora_decode_YUVout( &_theoraState, &yuv);
382             gotFrame = true;
383           }
384           else {
385 
386             PTRACE(1, "THEORA", "Decoder\tGot OGG non-keyframe data Packet but still waiting for keyframe");
387             flags = (_gotAGoodFrame ? requestIFrame : 0);
388             _gotAGoodFrame = false;
389             return 1;
390           }
391         }
392       }
393       else {
394 
395         PTRACE(1, "THEORA", "Decoder\tGot OGG data packet but still waiting for header and/or table Packets");
396         return 0;
397       }
398     }
399   }
400 
401   PTRACE(4, "THEORA", "Decoder\tNo more OGG packets to decode");
402 
403   if (gotFrame) {
404 
405     int size = _theoraInfo.width * _theoraInfo.height;
406     int frameBytes = (int) (size * 3 / 2);
407     PluginCodec_Video_FrameHeader * header = (PluginCodec_Video_FrameHeader *)dstRTP.GetPayloadPtr();
408 
409     PTRACE(4, "THEORA", "Decoder\tDecoded Frame with resolution: " << _theoraInfo.width << "x" << _theoraInfo.height);
410 
411     header->x = header->y = 0;
412     header->width = _theoraInfo.width;
413     header->height = _theoraInfo.height;
414 
415     unsigned int i = 0;
416     int width2 = (header->width >> 1);
417 
418     uint8_t* dstY = (uint8_t*) OPAL_VIDEO_FRAME_DATA_PTR(header);
419     uint8_t* dstU = (uint8_t*) dstY + size;
420     uint8_t* dstV = (uint8_t*) dstU + (size >> 2);
421 
422     uint8_t* srcY = yuv.y;
423     uint8_t* srcU = yuv.u;
424     uint8_t* srcV = yuv.v;
425 
426     for (i = 0 ; i < header->height ; i+=2) {
427 
428       memcpy (dstY, srcY, header->width);
429       srcY +=yuv.y_stride;
430       dstY +=header->width;
431 
432       memcpy (dstY, srcY, header->width);
433       srcY +=yuv.y_stride;
434       dstY +=header->width;
435 
436       memcpy(dstU, srcU, width2);
437       srcU+=yuv.uv_stride;
438       dstU+=width2;
439 
440       memcpy (dstV, srcV, width2);
441       srcV += yuv.uv_stride;
442       dstV +=width2;
443     }
444 
445     dstRTP.SetPayloadSize(sizeof(PluginCodec_Video_FrameHeader) + frameBytes);
446     dstRTP.SetTimestamp(srcRTP.GetTimestamp());
447     dstRTP.SetMarker(1);
448     dstLen = dstRTP.GetFrameLen();
449     flags = PluginCodec_ReturnCoderLastFrame;
450     _frameCounter++;
451     _gotAGoodFrame = true;
452     return 1;
453   }
454   else { /*gotFrame */
455 
456     PTRACE(1, "THEORA", "Decoder\tDid not get a decoded frame");
457     flags = (_gotAGoodFrame ? requestIFrame : 0);
458     _gotAGoodFrame = false;
459     return 1;
460   }
461 }
462 
463 /////////////////////////////////////////////////////////////////////////////
464 
465 const char*
theoraErrorMessage(int code)466 theoraErrorMessage(int code)
467 {
468   static const char *error;
469   static char errormsg [1024];
470 
471   switch (code) {
472     case OC_FAULT:
473       error = "General failure";
474       break;
475     case OC_EINVAL:
476       error = "Library encountered invalid internal data";
477       break;
478     case OC_DISABLED:
479       error = "Requested action is disabled";
480       break;
481     case OC_BADHEADER:
482       error = "Header packet was corrupt/invalid";
483       break;
484     case OC_NOTFORMAT:
485       error = "Packet is not a theora packet";
486       break;
487     case OC_VERSION:
488       error = "Bitstream version is not handled";
489       break;
490     case OC_IMPL:
491       error = "Feature or action not implemented";
492       break;
493     case OC_BADPACKET:
494       error = "Packet is corrupt";
495       break;
496     case OC_NEWPACKET:
497       error = "Packet is an (ignorable) unhandled extension";
498       break;
499     case OC_DUPFRAME:
500       error = "Packet is a dropped frame";
501       break;
502     default:
503       snprintf (errormsg, sizeof (errormsg), "%u", code);
504       return (errormsg);
505   }
506   snprintf (errormsg, sizeof(errormsg), "%s (%u)", error, code);
507   return (errormsg);
508 }
509 
num2str(int num)510 static char * num2str(int num)
511 {
512   char buf[20];
513   sprintf(buf, "%i", num);
514   return strdup(buf);
515 }
516 
517 /////////////////////////////////////////////////////////////////////////////
518 
get_codec_options(const struct PluginCodec_Definition * codec,void *,const char *,void * parm,unsigned * parmLen)519 static int get_codec_options(const struct PluginCodec_Definition * codec,
520                                                   void *,
521                                                   const char *,
522                                                   void * parm,
523                                                   unsigned * parmLen)
524 {
525     if (parmLen == NULL || parm == NULL || *parmLen != sizeof(struct PluginCodec_Option **))
526         return 0;
527 
528     *(const void **)parm = codec->userData;
529     *parmLen = 0; //FIXME
530     return 1;
531 }
532 
free_codec_options(const struct PluginCodec_Definition *,void *,const char *,void * parm,unsigned * parmLen)533 static int free_codec_options ( const struct PluginCodec_Definition *, void *, const char *, void * parm, unsigned * parmLen)
534 {
535   if (parmLen == NULL || parm == NULL || *parmLen != sizeof(char ***))
536     return 0;
537 
538   char ** strings = (char **) parm;
539   for (char ** string = strings; *string != NULL; string++)
540     free(*string);
541   free(strings);
542   return 1;
543 }
544 
valid_for_protocol(const struct PluginCodec_Definition *,void *,const char *,void * parm,unsigned * parmLen)545 static int valid_for_protocol ( const struct PluginCodec_Definition *, void *, const char *, void * parm, unsigned * parmLen)
546 {
547   if (parmLen == NULL || parm == NULL || *parmLen != sizeof(char *))
548     return 0;
549 
550   return (STRCMPI((const char *)parm, "sip") == 0) ? 1 : 0;
551 }
552 
553 /////////////////////////////////////////////////////////////////////////////
554 
create_encoder(const struct PluginCodec_Definition *)555 static void * create_encoder(const struct PluginCodec_Definition *)
556 {
557   return new theoraEncoderContext;
558 }
559 
destroy_encoder(const struct PluginCodec_Definition *,void * _context)560 static void destroy_encoder(const struct PluginCodec_Definition *, void * _context)
561 {
562   theoraEncoderContext * context = (theoraEncoderContext *)_context;
563   delete context;
564 }
565 
codec_encoder(const struct PluginCodec_Definition *,void * _context,const void * from,unsigned * fromLen,void * to,unsigned * toLen,unsigned int * flag)566 static int codec_encoder(const struct PluginCodec_Definition * ,
567                                            void * _context,
568                                      const void * from,
569                                        unsigned * fromLen,
570                                            void * to,
571                                        unsigned * toLen,
572                                    unsigned int * flag)
573 {
574   theoraEncoderContext * context = (theoraEncoderContext *)_context;
575   return context->EncodeFrames((const u_char *)from, *fromLen, (u_char *)to, *toLen, *flag);
576 }
577 
to_normalised_options(const struct PluginCodec_Definition *,void *,const char *,void * parm,unsigned * parmLen)578 static int to_normalised_options(const struct PluginCodec_Definition *, void *, const char *, void * parm, unsigned * parmLen)
579 {
580   if (parmLen == NULL || parm == NULL || *parmLen != sizeof(char ***))
581     return 0;
582 
583   int capWidth = 15;
584   int capHeight = 15;
585   int frameWidth = 352;
586   int frameHeight = 288;
587 
588   for (const char * const * option = *(const char * const * *)parm; *option != NULL; option += 2) {
589     if (STRCMPI(option[0], "CAP Width") == 0)
590       capWidth = atoi(option[1]);
591     else if (STRCMPI(option[0], "CAP Height") == 0)
592       capHeight = atoi(option[1]) ;
593     else if (STRCMPI(option[0], PLUGINCODEC_OPTION_FRAME_WIDTH) == 0)
594       frameWidth = atoi(option[1]);
595     else if (STRCMPI(option[0], PLUGINCODEC_OPTION_FRAME_HEIGHT) == 0)
596       frameHeight = atoi(option[1]);
597   }
598 
599   if ((capWidth == 15) || ( capHeight == 15)) {
600     capWidth = 640;
601     capHeight = 480;
602   }
603 
604   frameWidth = std::min(capWidth, frameWidth);
605   frameHeight = std::min(capHeight, frameHeight);
606 
607   frameWidth -= frameWidth % 16;
608   frameHeight -= frameHeight % 16;
609 
610   char ** options = (char **)calloc(5, sizeof(char *));
611   *(char ***)parm = options;
612   if (options == NULL)
613     return 0;
614 
615   options[ 0] = strdup(PLUGINCODEC_OPTION_FRAME_WIDTH);
616   options[ 1] = num2str(frameWidth);
617   options[ 2] = strdup(PLUGINCODEC_OPTION_FRAME_HEIGHT);
618   options[ 3] = num2str(frameHeight);
619 
620   return 1;
621 }
622 
to_customised_options(const struct PluginCodec_Definition *,void *,const char *,void * parm,unsigned * parmLen)623 static int to_customised_options(const struct PluginCodec_Definition *, void *, const char *, void * parm, unsigned * parmLen)
624 {
625   if (parmLen == NULL || parm == NULL || *parmLen != sizeof(char ***))
626     return 0;
627 
628   int capWidth = 352;
629   int capHeight = 288;
630   int maxWidth = 1280;
631   int maxHeight = 720;
632 
633   for (const char * const * option = *(const char * const * *)parm; *option != NULL; option += 2) {
634     if (STRCMPI(option[0], PLUGINCODEC_OPTION_MAX_RX_FRAME_WIDTH) == 0)
635       maxWidth = atoi(option[1]) - (atoi(option[1]) % 16); // FIXME
636     else if (STRCMPI(option[0], PLUGINCODEC_OPTION_MAX_RX_FRAME_HEIGHT) == 0)
637       maxHeight = atoi(option[1]) - (atoi(option[1]) % 16);
638     else if (STRCMPI(option[0], "CAP Width") == 0)
639       capWidth = atoi(option[1]);
640     else if (STRCMPI(option[0], "CAP Height") == 0)
641       capHeight = atoi(option[1]) ;
642 
643   }
644 
645   capWidth = std::min(capWidth, maxWidth);
646   capHeight = std::min(capHeight, maxHeight);
647 
648   capWidth -= capWidth % 16;
649   capHeight -= capHeight % 16;
650 
651   char ** options = (char **)calloc(5, sizeof(char *));
652   *(char ***)parm = options;
653   if (options == NULL)
654     return 0;
655 
656   options[ 0] = strdup("CAP Width");
657   options[ 1] = num2str(capWidth);
658   options[ 2] = strdup("CAP Height");
659   options[ 3] = num2str(capHeight);
660 
661   return 1;
662 
663 }
664 
encoder_set_options(const struct PluginCodec_Definition *,void * _context,const char *,void * parm,unsigned * parmLen)665 static int encoder_set_options(
666       const struct PluginCodec_Definition *,
667       void * _context,
668       const char *,
669       void * parm,
670       unsigned * parmLen)
671 {
672   theoraEncoderContext * context = (theoraEncoderContext *)_context;
673 
674   if (parmLen == NULL || *parmLen != sizeof(const char **)) return 0;
675 
676   context->Lock();
677   if (parm != NULL) {
678     const char ** options = (const char **)parm;
679     int i;
680     for (i = 0; options[i] != NULL; i += 2) {
681       if (STRCMPI(options[i], PLUGINCODEC_OPTION_TARGET_BIT_RATE) == 0)
682          context->SetTargetBitrate(atoi(options[i+1]));
683       if (STRCMPI(options[i], PLUGINCODEC_OPTION_FRAME_TIME) == 0)
684          context->SetFrameRate((int)(THEORA_CLOCKRATE / atoi(options[i+1])));
685       if (STRCMPI(options[i], PLUGINCODEC_OPTION_FRAME_HEIGHT) == 0)
686          context->SetFrameHeight(atoi(options[i+1]));
687       if (STRCMPI(options[i], PLUGINCODEC_OPTION_FRAME_WIDTH) == 0)
688          context->SetFrameWidth(atoi(options[i+1]));
689       if (STRCMPI(options[i], PLUGINCODEC_OPTION_MAX_FRAME_SIZE) == 0)
690 	 context->SetMaxRTPFrameSize (atoi(options[i+1]));
691       if (STRCMPI(options[i], PLUGINCODEC_OPTION_TX_KEY_FRAME_PERIOD) == 0)
692 	 context->SetMaxKeyFramePeriod (atoi(options[i+1]));
693       PTRACE(4, "THEORA", "Encoder\tOption " << options[i] << " = " << atoi(options[i+1]));
694     }
695     context->ApplyOptions();
696   }
697   context->Unlock();
698 
699   return 1;
700 }
701 
702 
encoder_get_output_data_size(const PluginCodec_Definition *,void *,const char *,void *,unsigned *)703 static int encoder_get_output_data_size(const PluginCodec_Definition *, void *, const char *, void *, unsigned *)
704 {
705   return 2000; //FIXME
706 }
707 
708 /////////////////////////////////////////////////////////////////////////////
709 
710 
create_decoder(const struct PluginCodec_Definition *)711 static void * create_decoder(const struct PluginCodec_Definition *)
712 {
713   return new theoraDecoderContext;
714 }
715 
destroy_decoder(const struct PluginCodec_Definition *,void * _context)716 static void destroy_decoder(const struct PluginCodec_Definition * /*codec*/, void * _context)
717 {
718   theoraDecoderContext * context = (theoraDecoderContext *)_context;
719   delete context;
720 }
721 
codec_decoder(const struct PluginCodec_Definition *,void * _context,const void * from,unsigned * fromLen,void * to,unsigned * toLen,unsigned int * flag)722 static int codec_decoder(const struct PluginCodec_Definition *,
723                                            void * _context,
724                                      const void * from,
725                                        unsigned * fromLen,
726                                            void * to,
727                                        unsigned * toLen,
728                                    unsigned int * flag)
729 {
730   theoraDecoderContext * context = (theoraDecoderContext *)_context;
731   return context->DecodeFrames((const u_char *)from, *fromLen, (u_char *)to, *toLen, *flag);
732 }
733 
decoder_get_output_data_size(const PluginCodec_Definition * codec,void *,const char *,void *,unsigned *)734 static int decoder_get_output_data_size(const PluginCodec_Definition * codec, void *, const char *, void *, unsigned *)
735 {
736   return sizeof(PluginCodec_Video_FrameHeader) + ((codec->parm.video.maxFrameWidth * codec->parm.video.maxFrameHeight * 3) / 2);
737 }
738 
739 /////////////////////////////////////////////////////////////////////////////
740 
741 extern "C" {
742 
PLUGIN_CODEC_IMPLEMENT(THEORA)743 PLUGIN_CODEC_IMPLEMENT(THEORA)
744 
745 PLUGIN_CODEC_DLL_API struct PluginCodec_Definition * PLUGIN_CODEC_GET_CODEC_FN(unsigned * count, unsigned version)
746 {
747   if (version < PLUGIN_CODEC_VERSION_VIDEO) {
748     *count = 0;
749     return NULL;
750   }
751   else {
752     *count = sizeof(theoraCodecDefn) / sizeof(struct PluginCodec_Definition);
753     return theoraCodecDefn;
754   }
755 
756 }
757 };
758