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