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
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>
40 #include <ConditionalMacros.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"
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>
76 #include <Components.k.h>
77 #include <ImageCodec.k.h>
78 #include <ImageCompression.k.h>
79 #include <ComponentDispatchHelper.c>
80 #endif /* __APPLE_CC__ */
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
);
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
);
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
));
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
);
127 Theora_ImageEncoderClose(TheoraGlobalsPtr globals
, ComponentInstance self
)
129 ComponentResult err
= noErr
;
130 dbg_printf("[ TE] >> [%08lx] :: Close()\n", (UInt32
) globals
);
134 if (globals
->yuv_buffer
)
135 free(globals
->yuv_buffer
);
141 dbg_printf("[ TE] < [%08lx] :: Close() = %ld\n", (UInt32
) globals
, err
);
145 ComponentResult
Theora_ImageEncoderVersion(TheoraGlobalsPtr globals
)
147 #pragma unused(globals)
148 return kTheora_imco_Version
;
152 Theora_ImageEncoderTarget(TheoraGlobalsPtr globals
, ComponentInstance target
)
154 globals
->target
= target
;
159 Theora_ImageEncoderGetCodecInfo(TheoraGlobalsPtr globals
, CodecInfo
*info
)
161 ComponentResult err
= noErr
;
162 dbg_printf("[ TE] >> [%08lx] :: GetCodecInfo()\n", (UInt32
) globals
);
167 CodecInfo
**tempCodecInfo
;
169 err
= GetComponentResource((Component
) globals
->self
,
170 codecInfoResourceType
, kTheoraEncoderResID
,
171 (Handle
*) &tempCodecInfo
);
173 *info
= **tempCodecInfo
;
174 DisposeHandle((Handle
) tempCodecInfo
);
178 dbg_printf("[ TE] < [%08lx] :: GetCodecInfo() = %ld\n", (UInt32
) globals
, err
);
183 Theora_ImageEncoderGetMaxCompressionSize(TheoraGlobalsPtr globals
,
184 PixMapHandle src
, const Rect
*srcRect
,
185 short depth
, CodecQ quality
,
188 ComponentResult err
= noErr
;
189 dbg_printf("[ TE] >> [%08lx] :: GetMaxCompressionSize()\n", (UInt32
) globals
);
194 *size
= (((srcRect
->right
- srcRect
->left
) + 0x0f) & ~0x0f) *
195 (((srcRect
->bottom
- srcRect
->top
) + 0x0f) & ~0x0f) *
199 dbg_printf("[ TE] < [%08lx] :: GetMaxCompressionSize() = %ld [%ld]\n", (UInt32
) globals
, err
, *size
);
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
);
219 Theora_ImageEncoderGetSettings(TheoraGlobalsPtr globals
, Handle settings
)
221 ComponentResult err
= noErr
;
223 dbg_printf("[ TE] >> [%08lx] :: GetSettings()\n", (UInt32
) globals
);
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
);
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;
261 dbg_printf("[ TE] < [%08lx] :: SetSettings() = %ld\n", (UInt32
) globals
, err
);
268 Theora_ImageEncoderPrepareToCompressFrames(TheoraGlobalsPtr globals
,
269 ICMCompressorSessionRef session
,
270 ICMCompressionSessionOptionsRef sessionOptions
,
271 ImageDescriptionHandle imageDescription
,
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' */
284 SInt32 widthRoundedUp
, heightRoundedUp
;
285 UInt32 buffer_size_needed
;
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
) {
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
);
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)
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
);
337 else if (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
);
349 // Modify imageDescription here if needed.
350 gammaLevel
= kQTCCIR601VideoGammaLevel
;
351 //gammaLevel = kQTUseSourceGammaLevel;
352 err
= ICMImageDescriptionSetProperty(imageDescription
,
353 kQTPropertyClass_ImageDescription
,
354 kICMImageDescriptionPropertyID_GammaLevel
,
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
);
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;
389 globals
->ti
.fps_numerator
= cs_fps
;
390 globals
->ti
.fps_denominator
= 0x010000;
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;
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 */
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;
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
;
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;
466 ogg_packet header
, header_tc
, header_cb
;
467 //UInt32 cookie_size;
470 Handle cookie_handle
;
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
);
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
);
516 Theora_ImageEncoderEncodeFrame(TheoraGlobalsPtr globals
,
517 ICMCompressorSourceFrameRef sourceFrame
,
520 ComponentResult err
= noErr
;
521 ICMCompressionFrameOptionsRef frameOptions
;
522 ICMMutableEncodedFrameRef encodedFrame
= NULL
;
523 UInt8
*dataPtr
= NULL
;
524 CVPixelBufferRef sourcePixelBuffer
= NULL
;
526 MediaSampleFlags mediaSampleFlags
;
527 ICMFrameType frameType
;
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
);
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
,
556 CVPixelBufferUnlockBaseAddress( sourcePixelBuffer
, 0 );
557 //dbg_printf("[ TE] > [%08lx] :: EncodeFrame() = %ld\n", (UInt32) globals, err);
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()
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);
581 mediaSampleFlags
= 0;
583 if (!theora_packet_iskeyframe(&op
)) {
584 mediaSampleFlags
|= mediaSampleNotSync
;
585 frameType
= kICMFrameType_P
;
587 mediaSampleFlags
|= mediaSampleDoesNotDependOnOthers
;
588 frameType
= kICMFrameType_I
;
591 err
= ICMEncodedFrameSetMediaSampleFlags(encodedFrame
, mediaSampleFlags
);
592 //dbg_printf("[ TE] . [%08lx] :: EncodeFrame() = %ld\n", (UInt32) globals, err);
596 err
= ICMEncodedFrameSetFrameType(encodedFrame
, frameType
);
597 //dbg_printf("[ TE] * [%08lx] :: EncodeFrame() = %ld\n", (UInt32) globals, err);
601 // Output the encoded frame.
602 err
= ICMCompressorSessionEmitEncodedFrame(globals
->session
, encodedFrame
, 1, &sourceFrame
);
603 //dbg_printf("[ TE] -> [%08lx] :: EncodeFrame() = %ld\n", (UInt32) globals, err);
608 // Since we created this, we must also release it.
609 ICMEncodedFrameRelease(encodedFrame
);
611 dbg_printf("[ TE] < [%08lx] :: EncodeFrame() = %ld\n", (UInt32
) globals
, err
);
616 Theora_ImageEncoderCompleteFrame(TheoraGlobalsPtr globals
,
617 ICMCompressorSourceFrameRef sourceFrame
,
620 ComponentResult err
= noErr
;
621 dbg_printf("[ TE] >> [%08lx] :: CompleteFrame()\n", (UInt32
) globals
);
623 dbg_printf("[ TE] < [%08lx] :: CompleteFrame() = %ld\n", (UInt32
) globals
, err
);
627 /* ========================================================================= */
630 Theora_ImageEncoderGetDITLForSize(TheoraGlobalsPtr globals
,
632 Point
*requestedSize
)
634 ComponentResult err
= noErr
;
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
);
649 err
= badComponentSelector
;
653 dbg_printf("[ TE] < [%08lx] :: GetDITLForSize() = %ld\n", (UInt32
) globals
, err
);
661 Theora_ImageEncoderDITLInstall(TheoraGlobalsPtr globals
,
665 ComponentResult err
= noErr
;
668 dbg_printf("[ TE] >> [%08lx] :: DITLInstall(%d)\n", (UInt32
) globals
, itemOffset
);
670 UInt32 v_sharp
= 3 - globals
->set_sharp
;
673 else if (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
);
688 Theora_ImageEncoderDITLEvent(TheoraGlobalsPtr globals
,
691 const EventRecord
*theEvent
,
695 ComponentResult err
= noErr
;
696 //dbg_printf("[ TE] >> [%08lx] :: DITLEvent(%d, %d)\n", (UInt32) globals, itemOffset, *itemHit);
700 //dbg_printf("[ TE] < [%08lx] :: DITLEvent() = %ld\n", (UInt32) globals, err);
705 Theora_ImageEncoderDITLItem(TheoraGlobalsPtr globals
,
710 ComponentResult err
= noErr
;
713 dbg_printf("[ TE] >> [%08lx] :: DITLItem()\n", (UInt32
) globals
);
715 switch (itemNum
- itemOffset
) {
717 GetDialogItemAsControl(d
, itemOffset
+ kItemSlow
, &cRef
);
718 SetControl32BitValue(cRef
, !GetControl32BitValue(cRef
));
724 dbg_printf("[ TE] < [%08lx] :: DITLItem() = %ld\n", (UInt32
) globals
, err
);
730 Theora_ImageEncoderDITLRemove(TheoraGlobalsPtr globals
,
734 ComponentResult err
= noErr
;
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
);
747 else if (v_sharp
< 0)
750 globals
->set_sharp
= v_sharp
;
752 dbg_printf("[ TE] < [%08lx] :: DITLRemove() = %ld\n", (UInt32
) globals
, err
);
758 Theora_ImageEncoderDITLValidateInput(TheoraGlobalsPtr globals
,
761 ComponentResult err
= noErr
;
763 dbg_printf("[ TE] >> [%08lx] :: DITLValidateInput()\n", (UInt32
) globals
);
768 dbg_printf("[ TE] < [%08lx] :: DITLValidateInput() = %ld\n", (UInt32
) globals
, err
);
774 /* ========================================================================= */
777 encode_frame(TheoraGlobalsPtr globals
, ICMCompressorSourceFrameRef sourceFrame
,
778 ICMFrameType frameType
)
780 ComponentResult err
= noErr
;
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
;
797 for (y
= 0; y
< height
; y
+= 2) {
799 UInt8
*b_bot
= base
+ b_2vuy_stride
;
800 UInt8
*y_top
= y_base
;
801 UInt8
*y_bot
= y_base
+ dst
->y_stride
;
804 for (x
= 0; x
< width
; x
+= 2) {
805 *cb
++ = (UInt8
) (((UInt16
) *b_top
++ + (UInt16
) *b_bot
++) >> 1);
808 *cr
++ = (UInt8
) (((UInt16
) *b_top
++ + (UInt16
) *b_bot
++) >> 1);
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
;
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
;
833 for (y
= 0; y
< height
; y
+= 2) {
835 UInt8
*b_bot
= base
+ b_2vuy_stride
;
836 UInt8
*y_top
= y_base
;
837 UInt8
*y_bot
= y_base
+ dst
->y_stride
;
840 for (x
= 0; x
< width
; x
+= 2) {
843 *cb
++ = (UInt8
) (((UInt16
) *b_top
++ + (UInt16
) *b_bot
++) >> 1);
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
;
857 // Utility to add an SInt32 to a CFMutableDictionary.
859 addNumberToDictionary(CFMutableDictionaryRef dictionary
, CFStringRef key
, SInt32 numberSInt32
)
861 CFNumberRef number
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &numberSInt32
);
864 CFDictionaryAddValue(dictionary
, key
, number
);
868 // Utility to add a double to a CFMutableDictionary.
870 addDoubleToDictionary(CFMutableDictionaryRef dictionary
, CFStringRef key
, double numberDouble
)
872 CFNumberRef number
= CFNumberCreate(NULL
, kCFNumberDoubleType
, &numberDouble
);
875 CFDictionaryAddValue(dictionary
, key
, number
);
880 create_pb_attribs(SInt32 width
, SInt32 height
,
881 const OSType
*pixelFormatList
, int pixelFormatCount
,
882 CFMutableDictionaryRef
*pixelBufferAttributesOut
)
884 ComponentResult err
= memFullErr
;
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
)
897 array
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
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
]);
908 CFArrayAppendValue(array
, number
);
914 CFDictionaryAddValue(pixelBufferAttributes
, kCVPixelBufferPixelFormatTypeKey
, array
);
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
;
938 addNumberToDictionary(pixelBufferAttributes
, kCVPixelBufferExtendedPixelsLeftKey
, x_l
);
940 addNumberToDictionary(pixelBufferAttributes
, kCVPixelBufferExtendedPixelsTopKey
, x_t
);
942 addNumberToDictionary(pixelBufferAttributes
, kCVPixelBufferExtendedPixelsRightKey
, x_r
);
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
);
958 *pixelBufferAttributesOut
= pixelBufferAttributes
;
959 pixelBufferAttributes
= NULL
;
962 if (pixelBufferAttributes
)
963 CFRelease(pixelBufferAttributes
);