Recognizes if input is ogg or not.
[xiph.git] / xiph-qt / Theora / src / TheoraEncoder.c
blob849f9c37f9b9eccb3ec8e3300d6cfeb78d911dba
1 /*
2 * TheoraEncoder.c
4 * Theora video encoder (ImageCodec) implementation.
7 * Copyright (c) 2006 Arek Korbik
9 * This file is part of XiphQT, the Xiph QuickTime Components.
11 * XiphQT is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * XiphQT is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with XiphQT; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 * Last modified: $Id$
31 * The implementation in this file is based on the 'ElectricImageCodec' and
32 * the 'ExampleIPBCodec' example QuickTime components.
36 #if defined(__APPLE_CC__)
37 #include <Carbon/Carbon.h>
38 #include <QuickTime/QuickTime.h>
39 #else
40 #include <ConditionalMacros.h>
41 #include <Endian.h>
42 #include <QuickTimeComponents.h>
43 #include <ImageCodec.h>
44 #endif /* __APPLE_CC__ */
46 #include "encoder_types.h"
47 #include "theora_versions.h"
49 #include "data_types.h"
50 #include "TheoraEncoder.h"
51 #include "debug.h"
53 #if !TARGET_OS_MAC
54 #undef pascal
55 #define pascal
56 #endif
58 // Setup required for ComponentDispatchHelper.c
59 #define IMAGECODEC_BASENAME() Theora_ImageEncoder
60 #define IMAGECODEC_GLOBALS() TheoraGlobalsPtr storage
62 #define CALLCOMPONENT_BASENAME() IMAGECODEC_BASENAME()
63 #define CALLCOMPONENT_GLOBALS() IMAGECODEC_GLOBALS()
65 #define COMPONENT_UPP_PREFIX() uppImageCodec
66 #define COMPONENT_DISPATCH_FILE "TheoraEncoderDispatch.h"
67 #define COMPONENT_SELECT_PREFIX() kImageCodec
70 #if defined(__APPLE_CC__)
71 #include <CoreServices/Components.k.h>
72 #include <QuickTime/ImageCodec.k.h>
73 #include <QuickTime/ImageCompression.k.h>
74 #include <QuickTime/ComponentDispatchHelper.c>
75 #else
76 #include <Components.k.h>
77 #include <ImageCodec.k.h>
78 #include <ImageCompression.k.h>
79 #include <ComponentDispatchHelper.c>
80 #endif /* __APPLE_CC__ */
83 ComponentResult
84 yuv_copy__422_to_420(void *b_2vuy, SInt32 b_2vuy_stride, size_t width, size_t height,
85 size_t offset_x, size_t offset_y, yuv_buffer *dst);
86 ComponentResult
87 vuy_copy__422_to_420(void *b_2vuy, SInt32 b_2vuy_stride, size_t width, size_t height,
88 size_t offset_x, size_t offset_y, yuv_buffer *dst);
89 ComponentResult create_pb_attribs(SInt32 width, SInt32 height, const OSType *pixelFormatList,
90 int pixelFormatCount, CFMutableDictionaryRef *pixelBufferAttributesOut);
93 ComponentResult
94 Theora_ImageEncoderOpen(TheoraGlobalsPtr globals, ComponentInstance self)
96 ComponentResult err = noErr;
97 dbg_printf("\n[ TE] >> [%08lx] :: Open()\n", (UInt32) globals);
99 globals = (TheoraGlobalsPtr) calloc(1, sizeof(TheoraGlobals));
100 if (!globals)
101 err = memFullErr;
103 if (!err) {
104 SetComponentInstanceStorage(self, (Handle) globals);
106 globals->self = self;
107 globals->target = self;
109 globals->yuv_buffer_size = 0;
110 globals->yuv_buffer = NULL;
112 globals->set_quality = codecNormalQuality;
113 globals->set_fps_numer = 0;
114 globals->set_fps_denom = 0;
115 globals->set_bitrate = 0;
116 globals->set_keyrate = 64;
118 globals->set_sharp = 2;
119 globals->set_quick = 1;
122 dbg_printf("[ TE] < [%08lx] :: Open() = %ld\n", (UInt32) globals, err);
123 return err;
126 ComponentResult
127 Theora_ImageEncoderClose(TheoraGlobalsPtr globals, ComponentInstance self)
129 ComponentResult err = noErr;
130 dbg_printf("[ TE] >> [%08lx] :: Close()\n", (UInt32) globals);
132 if (globals) {
134 if (globals->yuv_buffer)
135 free(globals->yuv_buffer);
137 free(globals);
138 globals = NULL;
141 dbg_printf("[ TE] < [%08lx] :: Close() = %ld\n", (UInt32) globals, err);
142 return err;
145 ComponentResult Theora_ImageEncoderVersion(TheoraGlobalsPtr globals)
147 #pragma unused(globals)
148 return kTheora_imco_Version;
151 ComponentResult
152 Theora_ImageEncoderTarget(TheoraGlobalsPtr globals, ComponentInstance target)
154 globals->target = target;
155 return noErr;
158 ComponentResult
159 Theora_ImageEncoderGetCodecInfo(TheoraGlobalsPtr globals, CodecInfo *info)
161 ComponentResult err = noErr;
162 dbg_printf("[ TE] >> [%08lx] :: GetCodecInfo()\n", (UInt32) globals);
164 if (info == NULL) {
165 err = paramErr;
166 } else {
167 CodecInfo **tempCodecInfo;
169 err = GetComponentResource((Component) globals->self,
170 codecInfoResourceType, kTheoraEncoderResID,
171 (Handle *) &tempCodecInfo);
172 if (!err) {
173 *info = **tempCodecInfo;
174 DisposeHandle((Handle) tempCodecInfo);
178 dbg_printf("[ TE] < [%08lx] :: GetCodecInfo() = %ld\n", (UInt32) globals, err);
179 return err;
182 ComponentResult
183 Theora_ImageEncoderGetMaxCompressionSize(TheoraGlobalsPtr globals,
184 PixMapHandle src, const Rect *srcRect,
185 short depth, CodecQ quality,
186 long *size)
188 ComponentResult err = noErr;
189 dbg_printf("[ TE] >> [%08lx] :: GetMaxCompressionSize()\n", (UInt32) globals);
191 if (!size) {
192 err = paramErr;
193 } else {
194 *size = (((srcRect->right - srcRect->left) + 0x0f) & ~0x0f) *
195 (((srcRect->bottom - srcRect->top) + 0x0f) & ~0x0f) *
196 3; // ?!?
199 dbg_printf("[ TE] < [%08lx] :: GetMaxCompressionSize() = %ld [%ld]\n", (UInt32) globals, err, *size);
200 return err;
203 ComponentResult
204 Theora_ImageEncoderRequestSettings(TheoraGlobalsPtr globals, Handle settings,
205 Rect *rp, ModalFilterUPP filterProc)
207 ComponentResult err = noErr;
209 dbg_printf("[ TE] >> [%08lx] :: RequestSettings()\n", (UInt32) globals);
211 err = badComponentSelector;
213 dbg_printf("[ TE] < [%08lx] :: RequestSettings() = %ld\n", (UInt32) globals, err);
215 return err;
218 ComponentResult
219 Theora_ImageEncoderGetSettings(TheoraGlobalsPtr globals, Handle settings)
221 ComponentResult err = noErr;
223 dbg_printf("[ TE] >> [%08lx] :: GetSettings()\n", (UInt32) globals);
225 if (!settings) {
226 err = paramErr;
227 } else {
228 SetHandleSize(settings, 8);
229 ((UInt32 *) *settings)[0] = 'XiTh';
230 ((UInt16 *) *settings)[2] = globals->set_sharp;
231 ((UInt16 *) *settings)[3] = globals->set_quick;
234 dbg_printf("[ TE] < [%08lx] :: GetSettings() = %ld\n", (UInt32) globals, err);
236 return err;
239 ComponentResult
240 Theora_ImageEncoderSetSettings(TheoraGlobalsPtr globals, Handle settings)
242 ComponentResult err = noErr;
244 dbg_printf("[ TE] >> [%08lx] :: SetSettings() %ld\n", (UInt32) globals, GetHandleSize(settings));
246 if (!settings || GetHandleSize(settings) == 0) {
247 globals->set_sharp = 2;
248 globals->set_quick = 1;
249 } else if (GetHandleSize(settings) == 8 && ((UInt32 *) *settings)[0] == 'XiTh') {
250 globals->set_sharp = ((UInt16 *) *settings)[2];
251 globals->set_quick = ((UInt16 *) *settings)[3];
253 if (globals->set_sharp > 2)
254 globals->set_sharp = 2;
255 if (globals->set_quick > 1)
256 globals->set_quick = 1;
257 } else {
258 err = paramErr;
261 dbg_printf("[ TE] < [%08lx] :: SetSettings() = %ld\n", (UInt32) globals, err);
263 return err;
267 ComponentResult
268 Theora_ImageEncoderPrepareToCompressFrames(TheoraGlobalsPtr globals,
269 ICMCompressorSessionRef session,
270 ICMCompressionSessionOptionsRef sessionOptions,
271 ImageDescriptionHandle imageDescription,
272 void *reserved,
273 CFDictionaryRef *compressorPixelBufferAttributesOut)
275 ComponentResult err = noErr;
276 dbg_printf("[ TE] >> [%08lx] :: PrepareToCompressFrames()\n", (UInt32) globals);
278 CFMutableDictionaryRef compressorPixelBufferAttributes = NULL;
279 //OSType pixelFormatList[] = { k422YpCbCr8PixelFormat, /* '2vuy' */
280 // kComponentVideoUnsigned }; /* 'yuvs' */
281 OSType pixelFormatList[] = { k422YpCbCr8PixelFormat }; /* '2vuy' */
282 Fixed gammaLevel;
283 //int frameIndex;
284 SInt32 widthRoundedUp, heightRoundedUp;
285 UInt32 buffer_size_needed;
286 int result;
288 Fixed cs_fps = 0;
289 CodecQ cs_quality = 0;
290 SInt32 cs_bitrate = 0;
291 SInt32 cs_keyrate = 0;
293 // Record the compressor session for later calls to the ICM.
294 // Note: this is NOT a CF type and should NOT be CFRetained or CFReleased.
295 globals->session = session;
297 // Retain the session options for later use.
298 ICMCompressionSessionOptionsRelease(globals->sessionOptions);
299 globals->sessionOptions = sessionOptions;
300 ICMCompressionSessionOptionsRetain(globals->sessionOptions);
302 if (globals->sessionOptions) {
303 UInt64 _frames = 0;
305 err = ICMCompressionSessionOptionsGetProperty(globals->sessionOptions,
306 kQTPropertyClass_ICMCompressionSessionOptions,
307 kICMCompressionSessionOptionsPropertyID_ExpectedFrameRate,
308 sizeof(cs_fps), &cs_fps, NULL);
309 dbg_printf("[ TE] fps [%08lx] :: PrepareToCompressFrames() = %ld, %f\n", (UInt32) globals, err, cs_fps / 65536.0f);
311 if (!err && cs_fps > 0.0) {
312 err = ICMCompressionSessionOptionsGetProperty(globals->sessionOptions,
313 kQTPropertyClass_ICMCompressionSessionOptions,
314 kICMCompressionSessionOptionsPropertyID_AverageDataRate,
315 sizeof(cs_bitrate), &cs_bitrate, NULL);
316 dbg_printf("[ TE] br [%08lx] :: PrepareToCompressFrames() = %ld, %ld\n", (UInt32) globals, err, cs_bitrate);
317 //cs_bitrate = 0;
320 if (cs_bitrate == 0) {
321 err = ICMCompressionSessionOptionsGetProperty(globals->sessionOptions,
322 kQTPropertyClass_ICMCompressionSessionOptions,
323 kICMCompressionSessionOptionsPropertyID_Quality,
324 sizeof(cs_quality), &cs_quality, NULL);
325 dbg_printf("[ TE] qu [%08lx] :: PrepareToCompressFrames() = %ld, %ld\n", (UInt32) globals, err, cs_quality);
326 if (!err && cs_quality == 0)
327 cs_quality = 1;
330 err = ICMCompressionSessionOptionsGetProperty(globals->sessionOptions,
331 kQTPropertyClass_ICMCompressionSessionOptions,
332 kICMCompressionSessionOptionsPropertyID_MaxKeyFrameInterval,
333 sizeof(cs_keyrate), &cs_keyrate, NULL);
334 dbg_printf("[ TE] kr [%08lx] :: PrepareToCompressFrames() = %ld, %ld\n", (UInt32) globals, err, cs_keyrate);
335 if (err)
336 cs_keyrate = 0;
337 else if (cs_keyrate > 32768)
338 cs_keyrate = 32768;
340 err = ICMCompressionSessionOptionsGetProperty(globals->sessionOptions,
341 kQTPropertyClass_ICMCompressionSessionOptions,
342 kICMCompressionSessionOptionsPropertyID_SourceFrameCount,
343 sizeof(_frames), &_frames, NULL);
344 dbg_printf("[ TE] f# [%08lx] :: PrepareToCompressFrames() = %ld, %lld\n", (UInt32) globals, err, _frames);
346 err = noErr;
349 // Modify imageDescription here if needed.
350 gammaLevel = kQTCCIR601VideoGammaLevel;
351 //gammaLevel = kQTUseSourceGammaLevel;
352 err = ICMImageDescriptionSetProperty(imageDescription,
353 kQTPropertyClass_ImageDescription,
354 kICMImageDescriptionPropertyID_GammaLevel,
355 sizeof(gammaLevel),
356 &gammaLevel);
357 if (err)
358 goto bail;
360 // Record the dimensions from the image description.
361 globals->width = (*imageDescription)->width;
362 globals->height = (*imageDescription)->height;
364 // Create a pixel buffer attributes dictionary.
365 err = create_pb_attribs(globals->width, globals->height,
366 pixelFormatList, sizeof(pixelFormatList) / sizeof(OSType),
367 &compressorPixelBufferAttributes);
368 if (err)
369 goto bail;
371 *compressorPixelBufferAttributesOut = compressorPixelBufferAttributes;
372 compressorPixelBufferAttributes = NULL;
374 globals->maxEncodedDataSize = globals->width * globals->height * 3;
376 globals->keyFrameCountDown = ICMCompressionSessionOptionsGetMaxKeyFrameInterval( globals->sessionOptions );
378 widthRoundedUp = (globals->width + 0x0f) & ~0x0f;
379 heightRoundedUp = (globals->height + 0x0f) & ~0x0f;
381 theora_info_init(&globals->ti);
382 globals->ti.width = widthRoundedUp;
383 globals->ti.height = heightRoundedUp;
384 globals->ti.frame_width = globals->width;
385 globals->ti.frame_height = globals->height;
386 globals->ti.offset_x = ((widthRoundedUp - globals->width) / 2) & ~0x01;
387 globals->ti.offset_y = ((heightRoundedUp - globals->height) / 2) & ~0x01;
388 if (cs_fps != 0) {
389 globals->ti.fps_numerator = cs_fps;
390 globals->ti.fps_denominator = 0x010000;
391 } else {
392 globals->ti.fps_numerator = 1;
393 globals->ti.fps_denominator = 1;
395 globals->ti.aspect_numerator = 1;
396 globals->ti.aspect_denominator = 1;
397 globals->ti.colorspace = OC_CS_UNSPECIFIED;
398 globals->ti.pixelformat = OC_PF_420;
399 if (cs_bitrate != 0) {
400 globals->ti.target_bitrate = cs_bitrate * 8;
401 } else {
402 globals->ti.target_bitrate = 0;
404 if (cs_quality != 0) {
405 globals->ti.quality = 64 * cs_quality / codecLosslessQuality;
406 } else if (cs_bitrate == 0) {
407 globals->ti.quality = 32; /* medium quality */
408 } else {
409 globals->ti.quality = 0;
412 globals->ti.quick_p = globals->set_quick;
413 globals->ti.sharpness = globals->set_sharp;
415 globals->ti.dropframes_p = 0;
416 globals->ti.keyframe_auto_p = 1;
417 if (cs_keyrate == 0) {
418 globals->ti.keyframe_frequency = 64;
419 globals->ti.keyframe_frequency_force = 64;
420 globals->ti.keyframe_mindistance = 8;
421 } else {
422 globals->ti.keyframe_frequency = cs_keyrate;
423 globals->ti.keyframe_frequency_force = cs_keyrate;
424 globals->ti.keyframe_mindistance = 8;
425 if (globals->ti.keyframe_mindistance > cs_keyrate)
426 globals->ti.keyframe_mindistance = cs_keyrate;
428 globals->ti.keyframe_data_target_bitrate = globals->ti.target_bitrate * 1.5;
429 globals->ti.keyframe_auto_threshold = 80;
430 globals->ti.noise_sensitivity = 1;
432 result = theora_encode_init(&globals->ts, &globals->ti);
433 dbg_printf("[ TE] i [%08lx] :: PrepareToCompressFrames() = %ld\n", (UInt32) globals, result);
434 //theora_info_clear(ti);
436 buffer_size_needed = globals->ti.width * globals->ti.height * 3 / 2;
438 if (globals->yuv_buffer) {
439 if (globals->yuv_buffer_size < buffer_size_needed) {
440 globals->yuv_buffer = realloc(globals->yuv_buffer, buffer_size_needed);
441 globals->yuv_buffer_size = buffer_size_needed;
443 } else {
444 globals->yuv_buffer_size = buffer_size_needed;
445 globals->yuv_buffer = malloc(buffer_size_needed);
448 globals->yuv.y_width = globals->ti.width;
449 globals->yuv.y_height = globals->ti.height;
450 globals->yuv.y_stride = globals->ti.width;
452 globals->yuv.uv_width = globals->ti.width / 2;
453 globals->yuv.uv_height = globals->ti.height / 2;
454 globals->yuv.uv_stride = globals->ti.width / 2;
456 globals->yuv.y = &globals->yuv_buffer[0];
457 globals->yuv.u = &globals->yuv_buffer[0] + globals->ti.width * globals->ti.height;
458 globals->yuv.v = &globals->yuv_buffer[0] + globals->ti.width * globals->ti.height * 5/4;
462 if (err)
463 goto bail;
466 ogg_packet header, header_tc, header_cb;
467 //UInt32 cookie_size;
468 UInt8 *cookie;
469 UInt32 *qtatom;
470 Handle cookie_handle;
471 UInt32 atomhead[2];
473 theora_encode_header(&globals->ts, &header);
474 theora_comment_init(&globals->tc);
476 cookie_handle = NewHandleClear(2 * sizeof(UInt32));
477 cookie = (UInt8 *) *cookie_handle;
479 qtatom = (UInt32 *) cookie;
480 *qtatom++ = EndianU32_NtoB(header.bytes + 2 * sizeof(UInt32));
481 *qtatom = EndianU32_NtoB(kCookieTypeTheoraHeader);
482 PtrAndHand(header.packet, cookie_handle, header.bytes);
484 theora_encode_comment(&globals->tc, &header_tc);
485 atomhead[0] = EndianU32_NtoB(header_tc.bytes + 2 * sizeof(UInt32));
486 atomhead[1] = EndianU32_NtoB(kCookieTypeTheoraComments);
487 PtrAndHand(atomhead, cookie_handle, sizeof(atomhead));
488 PtrAndHand(header_tc.packet, cookie_handle, header_tc.bytes);
490 theora_encode_tables(&globals->ts, &header_cb);
491 atomhead[0] = EndianU32_NtoB(header_cb.bytes + 2 * sizeof(UInt32));
492 atomhead[1] = EndianU32_NtoB(kCookieTypeTheoraCodebooks);
493 PtrAndHand(atomhead, cookie_handle, sizeof(atomhead));
494 PtrAndHand(header_cb.packet, cookie_handle, header_cb.bytes);
496 atomhead[0] = EndianU32_NtoB(2 * sizeof(UInt32));
497 atomhead[1] = EndianU32_NtoB(kAudioTerminatorAtomType);
498 PtrAndHand(atomhead, cookie_handle, sizeof(atomhead));
500 err = AddImageDescriptionExtension(imageDescription, cookie_handle, kSampleDescriptionExtensionTheora);
501 DisposeHandle(cookie_handle);
502 free(header_tc.packet);
505 bail:
506 if (compressorPixelBufferAttributes)
507 CFRelease(compressorPixelBufferAttributes);
510 dbg_printf("[ TE] < [%08lx] :: PrepareToCompressFrames() = %ld [fps: %f, q: 0x%04lx, kr: %ld, br: %ld, #: %d, O: %d]\n",
511 (UInt32) globals, err, cs_fps / 65536.0, cs_quality, cs_keyrate, cs_bitrate, globals->set_sharp, !globals->set_quick);
512 return err;
515 ComponentResult
516 Theora_ImageEncoderEncodeFrame(TheoraGlobalsPtr globals,
517 ICMCompressorSourceFrameRef sourceFrame,
518 UInt32 flags)
520 ComponentResult err = noErr;
521 ICMCompressionFrameOptionsRef frameOptions;
522 ICMMutableEncodedFrameRef encodedFrame = NULL;
523 UInt8 *dataPtr = NULL;
524 CVPixelBufferRef sourcePixelBuffer = NULL;
525 ogg_packet op;
526 MediaSampleFlags mediaSampleFlags;
527 ICMFrameType frameType;
528 int result;
531 dbg_printf("[ TE] >> [%08lx] :: EncodeFrame()\n", (UInt32) globals);
533 /* 1. copy pixels to a theora's yuv_buffer
534 * 2. theora_encode_YUVin()
535 * 3. theora_encode_packetout()
536 * 4. copy the packet's body back to QT's encodedBuffer */
538 memset(&op, 0, sizeof(op));
540 // Create the buffer for the encoded frame.
541 err = ICMEncodedFrameCreateMutable(globals->session, sourceFrame, globals->maxEncodedDataSize, &encodedFrame);
542 dbg_printf("[ TE] ? [%08lx] :: EncodeFrame() = %ld [%ld]\n", (UInt32) globals, err, globals->maxEncodedDataSize);
543 if (err)
544 goto bail;
546 dataPtr = ICMEncodedFrameGetDataPtr(encodedFrame);
548 // Transfer the source frame into glob->currentFrame, converting it from chunky YUV422 to planar YUV420.
549 sourcePixelBuffer = ICMCompressorSourceFrameGetPixelBuffer(sourceFrame);
550 CVPixelBufferLockBaseAddress(sourcePixelBuffer, 0);
551 err = yuv_copy__422_to_420(CVPixelBufferGetBaseAddress(sourcePixelBuffer),
552 CVPixelBufferGetBytesPerRow(sourcePixelBuffer),
553 globals->width, globals->height,
554 globals->ti.offset_x, globals->ti.offset_y,
555 &globals->yuv);
556 CVPixelBufferUnlockBaseAddress( sourcePixelBuffer, 0 );
557 //dbg_printf("[ TE] > [%08lx] :: EncodeFrame() = %ld\n", (UInt32) globals, err);
558 if (err)
559 goto bail;
561 result = theora_encode_YUVin(&globals->ts, &globals->yuv);
562 //dbg_printf("[ TE] Y> [%08lx] :: EncodeFrame() = %d\n", (UInt32) globals, result);
564 result = theora_encode_packetout(&globals->ts, 0, &op);
565 //dbg_printf("[ TE] T< [%08lx] :: EncodeFrame() = %d\n", (UInt32) globals, result);
568 /* The first byte in the returned encoded frame packet is left
569 empty, the actuall data starting at position 1 and the size
570 reported is increased by one to compensate for the
571 pre-padding. This is because QT doesn't allow zero-size
572 packets. See also TheoraDecoder.c, Theora_ImageCodecBeginBand()
573 function. */
574 memcpy(dataPtr + 1, op.packet, op.bytes);
576 err = ICMEncodedFrameSetDataSize(encodedFrame, op.bytes + 1);
577 dbg_printf("[ TE] = [%08lx] :: EncodeFrame() = %ld [%ld (%ld)]\n", (UInt32) globals, err, op.bytes, op.bytes + 1);
578 if (err)
579 goto bail;
581 mediaSampleFlags = 0;
583 if (!theora_packet_iskeyframe(&op)) {
584 mediaSampleFlags |= mediaSampleNotSync;
585 frameType = kICMFrameType_P;
586 } else {
587 mediaSampleFlags |= mediaSampleDoesNotDependOnOthers;
588 frameType = kICMFrameType_I;
591 err = ICMEncodedFrameSetMediaSampleFlags(encodedFrame, mediaSampleFlags);
592 //dbg_printf("[ TE] . [%08lx] :: EncodeFrame() = %ld\n", (UInt32) globals, err);
593 if (err)
594 goto bail;
596 err = ICMEncodedFrameSetFrameType(encodedFrame, frameType);
597 //dbg_printf("[ TE] * [%08lx] :: EncodeFrame() = %ld\n", (UInt32) globals, err);
598 if (err)
599 goto bail;
601 // Output the encoded frame.
602 err = ICMCompressorSessionEmitEncodedFrame(globals->session, encodedFrame, 1, &sourceFrame);
603 //dbg_printf("[ TE] -> [%08lx] :: EncodeFrame() = %ld\n", (UInt32) globals, err);
604 if (err)
605 goto bail;
607 bail:
608 // Since we created this, we must also release it.
609 ICMEncodedFrameRelease(encodedFrame);
611 dbg_printf("[ TE] < [%08lx] :: EncodeFrame() = %ld\n", (UInt32) globals, err);
612 return err;
615 ComponentResult
616 Theora_ImageEncoderCompleteFrame(TheoraGlobalsPtr globals,
617 ICMCompressorSourceFrameRef sourceFrame,
618 UInt32 flags )
620 ComponentResult err = noErr;
621 dbg_printf("[ TE] >> [%08lx] :: CompleteFrame()\n", (UInt32) globals);
623 dbg_printf("[ TE] < [%08lx] :: CompleteFrame() = %ld\n", (UInt32) globals, err);
624 return err;
627 /* ========================================================================= */
629 ComponentResult
630 Theora_ImageEncoderGetDITLForSize(TheoraGlobalsPtr globals,
631 Handle *ditl,
632 Point *requestedSize)
634 ComponentResult err = noErr;
635 Handle h = NULL;
637 dbg_printf("[ TE] >> [%08lx] :: GetDITLForSize()\n", (UInt32) globals);
639 switch (requestedSize->h) {
640 case kSGSmallestDITLSize:
641 GetComponentResource((Component) globals->self, FOUR_CHAR_CODE('DITL'),
642 kTheoraEncoderDITLResID, &h);
643 if (h != NULL)
644 *ditl = h;
645 else
646 err = resNotFound;
647 break;
648 default:
649 err = badComponentSelector;
650 break;
653 dbg_printf("[ TE] < [%08lx] :: GetDITLForSize() = %ld\n", (UInt32) globals, err);
654 return err;
657 #define kItemSharp 1
658 #define kItemSlow 2
660 ComponentResult
661 Theora_ImageEncoderDITLInstall(TheoraGlobalsPtr globals,
662 DialogRef d,
663 short itemOffset)
665 ComponentResult err = noErr;
666 ControlRef cRef;
668 dbg_printf("[ TE] >> [%08lx] :: DITLInstall(%d)\n", (UInt32) globals, itemOffset);
670 UInt32 v_sharp = 3 - globals->set_sharp;
671 if (v_sharp > 3)
672 v_sharp = 3;
673 else if (v_sharp < 1)
674 v_sharp = 1;
676 GetDialogItemAsControl(d, kItemSlow + itemOffset, &cRef);
677 SetControl32BitValue(cRef, (UInt32) (globals->set_quick == 0));
679 GetDialogItemAsControl(d, kItemSharp + itemOffset, &cRef);
680 SetControl32BitValue(cRef, (UInt32) v_sharp);
682 dbg_printf("[ TE] < [%08lx] :: DITLInstall() = %ld\n", (UInt32) globals, err);
684 return err;
687 ComponentResult
688 Theora_ImageEncoderDITLEvent(TheoraGlobalsPtr globals,
689 DialogRef d,
690 short itemOffset,
691 const EventRecord *theEvent,
692 short *itemHit,
693 Boolean *handled)
695 ComponentResult err = noErr;
696 //dbg_printf("[ TE] >> [%08lx] :: DITLEvent(%d, %d)\n", (UInt32) globals, itemOffset, *itemHit);
698 *handled = false;
700 //dbg_printf("[ TE] < [%08lx] :: DITLEvent() = %ld\n", (UInt32) globals, err);
701 return err;
704 ComponentResult
705 Theora_ImageEncoderDITLItem(TheoraGlobalsPtr globals,
706 DialogRef d,
707 short itemOffset,
708 short itemNum)
710 ComponentResult err = noErr;
711 ControlRef cRef;
713 dbg_printf("[ TE] >> [%08lx] :: DITLItem()\n", (UInt32) globals);
715 switch (itemNum - itemOffset) {
716 case kItemSlow:
717 GetDialogItemAsControl(d, itemOffset + kItemSlow, &cRef);
718 SetControl32BitValue(cRef, !GetControl32BitValue(cRef));
719 break;
720 case kItemSharp:
721 break;
724 dbg_printf("[ TE] < [%08lx] :: DITLItem() = %ld\n", (UInt32) globals, err);
726 return err;
729 ComponentResult
730 Theora_ImageEncoderDITLRemove(TheoraGlobalsPtr globals,
731 DialogRef d,
732 short itemOffset)
734 ComponentResult err = noErr;
735 ControlRef cRef;
736 UInt32 v_sharp;
738 dbg_printf("[ TE] >> [%08lx] :: DITLRemove()\n", (UInt32) globals);
740 GetDialogItemAsControl(d, kItemSlow + itemOffset, &cRef);
741 globals->set_quick = GetControl32BitValue(cRef) == 0 ? 1 : 0;
743 GetDialogItemAsControl(d, kItemSharp + itemOffset, &cRef);
744 v_sharp = 3 - GetControl32BitValue(cRef);
745 if (v_sharp > 2)
746 v_sharp = 2;
747 else if (v_sharp < 0)
748 v_sharp = 0;
750 globals->set_sharp = v_sharp;
752 dbg_printf("[ TE] < [%08lx] :: DITLRemove() = %ld\n", (UInt32) globals, err);
754 return err;
757 ComponentResult
758 Theora_ImageEncoderDITLValidateInput(TheoraGlobalsPtr globals,
759 Boolean *ok)
761 ComponentResult err = noErr;
763 dbg_printf("[ TE] >> [%08lx] :: DITLValidateInput()\n", (UInt32) globals);
765 if (ok)
766 *ok = true;
768 dbg_printf("[ TE] < [%08lx] :: DITLValidateInput() = %ld\n", (UInt32) globals, err);
770 return err;
774 /* ========================================================================= */
776 ComponentResult
777 encode_frame(TheoraGlobalsPtr globals, ICMCompressorSourceFrameRef sourceFrame,
778 ICMFrameType frameType)
780 ComponentResult err = noErr;
782 return err;
785 ComponentResult
786 yuv_copy__422_to_420(void *b_2vuy, SInt32 b_2vuy_stride, size_t width, size_t height,
787 size_t offset_x, size_t offset_y, yuv_buffer *dst)
789 UInt8 *base = b_2vuy;
790 size_t off_x = offset_x & ~0x01, off_y = offset_y & ~0x01;
791 size_t off_x2 = offset_x >> 1, off_y2 = offset_y >> 1;
792 UInt8 *y_base = dst->y + off_y * dst->y_stride + off_x;
793 UInt8 *cb_base = dst->u + off_y2 * dst->uv_stride + off_x2;
794 UInt8 *cr_base = dst->v + off_y2 * dst->uv_stride + off_x2;
795 size_t x, y;
797 for (y = 0; y < height; y += 2) {
798 UInt8 *b_top = base;
799 UInt8 *b_bot = base + b_2vuy_stride;
800 UInt8 *y_top = y_base;
801 UInt8 *y_bot = y_base + dst->y_stride;
802 UInt8 *cb = cb_base;
803 UInt8 *cr = cr_base;
804 for (x = 0; x < width; x += 2) {
805 *cb++ = (UInt8) (((UInt16) *b_top++ + (UInt16) *b_bot++) >> 1);
806 *y_top++ = *b_top++;
807 *y_bot++ = *b_bot++;
808 *cr++ = (UInt8) (((UInt16) *b_top++ + (UInt16) *b_bot++) >> 1);
809 *y_top++ = *b_top++;
810 *y_bot++ = *b_bot++;
812 base += 2 * b_2vuy_stride;
813 y_base += 2 * dst->y_stride;
814 cb_base += dst->uv_stride;
815 cr_base += dst->uv_stride;
818 return noErr;
821 ComponentResult
822 vuy_copy__422_to_420(void *b_2vuy, SInt32 b_2vuy_stride, size_t width, size_t height,
823 size_t offset_x, size_t offset_y, yuv_buffer *dst)
825 UInt8 *base = b_2vuy;
826 size_t off_x = offset_x & ~0x01, off_y = offset_y & ~0x01;
827 size_t off_x2 = offset_x >> 1, off_y2 = offset_y >> 1;
828 UInt8 *y_base = dst->y + off_y * dst->y_stride + off_x;
829 UInt8 *cb_base = dst->u + off_y2 * dst->uv_stride + off_x2;
830 UInt8 *cr_base = dst->v + off_y2 * dst->uv_stride + off_x2;
831 size_t x, y;
833 for (y = 0; y < height; y += 2) {
834 UInt8 *b_top = base;
835 UInt8 *b_bot = base + b_2vuy_stride;
836 UInt8 *y_top = y_base;
837 UInt8 *y_bot = y_base + dst->y_stride;
838 UInt8 *cb = cb_base;
839 UInt8 *cr = cr_base;
840 for (x = 0; x < width; x += 2) {
841 *y_top++ = *b_top++;
842 *y_bot++ = *b_bot++;
843 *cb++ = (UInt8) (((UInt16) *b_top++ + (UInt16) *b_bot++) >> 1);
844 *y_top++ = *b_top++;
845 *y_bot++ = *b_bot++;
846 *cr++ = (UInt8) (((UInt16) *b_top++ + (UInt16) *b_bot++) >> 1);
848 base += 2 * b_2vuy_stride;
849 y_base += 2 * dst->y_stride;
850 cb_base += dst->uv_stride;
851 cr_base += dst->uv_stride;
854 return noErr;
857 // Utility to add an SInt32 to a CFMutableDictionary.
858 static void
859 addNumberToDictionary(CFMutableDictionaryRef dictionary, CFStringRef key, SInt32 numberSInt32)
861 CFNumberRef number = CFNumberCreate(NULL, kCFNumberSInt32Type, &numberSInt32);
862 if (!number)
863 return;
864 CFDictionaryAddValue(dictionary, key, number);
865 CFRelease(number);
868 // Utility to add a double to a CFMutableDictionary.
869 static void
870 addDoubleToDictionary(CFMutableDictionaryRef dictionary, CFStringRef key, double numberDouble)
872 CFNumberRef number = CFNumberCreate(NULL, kCFNumberDoubleType, &numberDouble);
873 if (!number)
874 return;
875 CFDictionaryAddValue(dictionary, key, number);
876 CFRelease(number);
879 ComponentResult
880 create_pb_attribs(SInt32 width, SInt32 height,
881 const OSType *pixelFormatList, int pixelFormatCount,
882 CFMutableDictionaryRef *pixelBufferAttributesOut)
884 ComponentResult err = memFullErr;
885 int i;
886 CFMutableDictionaryRef pixelBufferAttributes = NULL;
887 CFNumberRef number = NULL;
888 CFMutableArrayRef array = NULL;
889 SInt32 widthRoundedUp, heightRoundedUp, x_l, x_r, x_t, x_b;
891 pixelBufferAttributes =
892 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
893 &kCFTypeDictionaryValueCallBacks);
894 if (!pixelBufferAttributes)
895 goto bail;
897 array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
898 if (!array)
899 goto bail;
901 // Under kCVPixelBufferPixelFormatTypeKey, add the list of source pixel formats.
902 // This can be a CFNumber or a CFArray of CFNumbers.
903 for (i = 0; i < pixelFormatCount; i++) {
904 number = CFNumberCreate(NULL, kCFNumberSInt32Type, &pixelFormatList[i]);
905 if (!number)
906 goto bail;
908 CFArrayAppendValue(array, number);
910 CFRelease(number);
911 number = NULL;
914 CFDictionaryAddValue(pixelBufferAttributes, kCVPixelBufferPixelFormatTypeKey, array);
915 CFRelease(array);
916 array = NULL;
918 // Add kCVPixelBufferWidthKey and kCVPixelBufferHeightKey to specify the dimensions
919 // of the source pixel buffers. Normally this is the same as the compression target dimensions.
920 addNumberToDictionary(pixelBufferAttributes, kCVPixelBufferWidthKey, width);
921 addNumberToDictionary(pixelBufferAttributes, kCVPixelBufferHeightKey, height);
923 // If you want to require that extra scratch pixels be allocated on the edges of source pixel buffers,
924 // add the kCVPixelBufferExtendedPixels{Left,Top,Right,Bottom}Keys to indicate how much.
925 // Internally our encoded can only support multiples of 16x16 macroblocks;
926 // we will round the compression dimensions up to a multiple of 16x16 and encode that size.
927 // (Note that if your compressor needs to copy the pixels anyhow (eg, in order to convert to a different
928 // format) you may get better performance if your copy routine does not require extended pixels.)
930 widthRoundedUp = (width + 0x0f) & ~0x0f;
931 heightRoundedUp = (height + 0x0f) & ~0x0f;
932 x_l = ((widthRoundedUp - width) / 2) & ~0x01;
933 x_t = ((heightRoundedUp - height) / 2) & ~0x01;
934 x_r = widthRoundedUp - width - x_l;
935 x_b = heightRoundedUp - height - x_t;
937 if (x_l)
938 addNumberToDictionary(pixelBufferAttributes, kCVPixelBufferExtendedPixelsLeftKey, x_l);
939 if (x_t)
940 addNumberToDictionary(pixelBufferAttributes, kCVPixelBufferExtendedPixelsTopKey, x_t);
941 if (x_r)
942 addNumberToDictionary(pixelBufferAttributes, kCVPixelBufferExtendedPixelsRightKey, x_r);
943 if (x_b)
944 addNumberToDictionary(pixelBufferAttributes, kCVPixelBufferExtendedPixelsBottomKey, x_b);
947 // Altivec code is most efficient reading data aligned at addresses that are multiples of 16.
948 // Pretending that we have some altivec code, we set kCVPixelBufferBytesPerRowAlignmentKey to
949 // ensure that each row of pixels starts at a 16-byte-aligned address.
950 addNumberToDictionary(pixelBufferAttributes, kCVPixelBufferBytesPerRowAlignmentKey, 16);
952 // This codec accepts YCbCr input in the form of '2vuy' format pixel buffers.
953 // We recommend explicitly defining the gamma level and YCbCr matrix that should be used.
954 addDoubleToDictionary(pixelBufferAttributes, kCVImageBufferGammaLevelKey, 2.2);
955 CFDictionaryAddValue(pixelBufferAttributes, kCVImageBufferYCbCrMatrixKey, kCVImageBufferYCbCrMatrix_ITU_R_601_4);
957 err = noErr;
958 *pixelBufferAttributesOut = pixelBufferAttributes;
959 pixelBufferAttributes = NULL;
961 bail:
962 if (pixelBufferAttributes)
963 CFRelease(pixelBufferAttributes);
965 if (number)
966 CFRelease(number);
968 if (array)
969 CFRelease(array);
971 return err;