source: trunk/TextSubCodec.c @ 1108

Revision 1108, 18.5 KB checked in by gbooker, 5 years ago (diff)

New system for defining resources. Hopefully this makes more sense

  • Property svn:executable set to *
Line 
1/*
2 * TextSubCodec.c
3 * Created by David Conrad on 3/21/06.
4 *
5 * This file is part of Perian.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#if __MACH__
23    #include <Carbon/Carbon.h>
24    #include <QuickTime/QuickTime.h>
25#else
26    #include <ConditionalMacros.h>
27    #include <Endian.h>
28    #include <ImageCodec.h>
29#endif
30
31#include "PerianResourceIDs.h"
32#include "SubATSUIRenderer.h"
33
34// Constants
35const UInt8 kNumPixelFormatsSupportedTextSub = 2;
36
37// Data structures
38typedef struct TextSubGlobalsRecord {
39        ComponentInstance               self;
40        ComponentInstance               delegateComponent;
41        ComponentInstance               target;
42        OSType**                                wantedDestinationPixelTypeH;
43        ImageCodecMPDrawBandUPP drawBandUPP;
44       
45        CGColorSpaceRef         colorSpace;
46
47        SubtitleRendererPtr             ssa;
48        Boolean                                 translateSRT;
49} TextSubGlobalsRecord, *TextSubGlobals;
50
51typedef struct {
52        long            width;
53        long            height;
54        long            depth;
55    long        dataSize;
56   
57    Ptr             baseAddr;
58        OSType                  pixelFormat;
59} TextSubDecompressRecord;
60
61// Setup required for ComponentDispatchHelper.c
62#define IMAGECODEC_BASENAME()           TextSubCodec
63#define IMAGECODEC_GLOBALS()            TextSubGlobals storage
64
65#define CALLCOMPONENT_BASENAME()        IMAGECODEC_BASENAME()
66#define CALLCOMPONENT_GLOBALS()         IMAGECODEC_GLOBALS()
67
68#define COMPONENT_UPP_PREFIX()          uppImageCodec
69#define COMPONENT_DISPATCH_FILE         "TextSubCodecDispatch.h"
70#define COMPONENT_SELECT_PREFIX()       kImageCodec
71
72#define GET_DELEGATE_COMPONENT()        (storage->delegateComponent)
73
74
75#if __MACH__
76        #include <CoreServices/Components.k.h>
77        #include <QuickTime/ImageCodec.k.h>
78        #include <QuickTime/ComponentDispatchHelper.c>
79#else
80        #include <Components.k.h>
81        #include <ImageCodec.k.h>
82        #include <ComponentDispatchHelper.c>
83#endif
84       
85static CFMutableStringRef CFStringCreateMutableWithBytes(CFAllocatorRef alloc, char *cStr, size_t size, CFStringEncoding encoding) {
86        CFStringRef                s1 = CFStringCreateWithBytes(alloc,(UInt8*)cStr,size,encoding,false);
87        if (!s1) return NULL;
88        CFMutableStringRef s2 = CFStringCreateMutableCopy(alloc,0,s1);
89        CFRelease(s1);
90        return s2;
91}
92
93/* -- This Image Decompressor Uses the Base Image Decompressor Component --
94        The base image decompressor is an Apple-supplied component
95        that makes it easier for developers to create new decompressors.
96        The base image decompressor does most of the housekeeping and
97        interface functions required for a QuickTime decompressor component,
98        including scheduling for asynchronous decompression.
99*/
100
101// Component Open Request - Required
102pascal ComponentResult TextSubCodecOpen(TextSubGlobals glob, ComponentInstance self)
103{
104        ComponentResult err;
105
106        // Allocate memory for our globals, set them up and inform the component manager that we've done so
107        glob = (TextSubGlobals)NewPtrClear(sizeof(TextSubGlobalsRecord));
108        if (err = MemError()) goto bail;
109
110        SetComponentInstanceStorage(self, (Handle)glob);
111
112        glob->self = self;
113        glob->target = self;
114        glob->wantedDestinationPixelTypeH = (OSType **)NewHandle(sizeof(OSType) * (kNumPixelFormatsSupportedTextSub + 1));
115        if (err = MemError()) goto bail;
116        glob->drawBandUPP = NULL;
117        glob->ssa = NULL;
118        glob->colorSpace = NULL;
119        glob->translateSRT = true;
120       
121        // Open and target an instance of the base decompressor as we delegate
122        // most of our calls to the base decompressor instance
123        err = OpenADefaultComponent(decompressorComponentType, kBaseCodecType, &glob->delegateComponent);
124        if (err) goto bail;
125
126        ComponentSetTarget(glob->delegateComponent, self);
127
128bail:
129        return err;
130}
131
132// Component Close Request - Required
133pascal ComponentResult TextSubCodecClose(TextSubGlobals glob, ComponentInstance self)
134{
135        // Make sure to close the base component and dealocate our storage
136        if (glob) {
137                if (glob->delegateComponent) {
138                        CloseComponent(glob->delegateComponent);
139                }
140                if (glob->wantedDestinationPixelTypeH) {
141                        DisposeHandle((Handle)glob->wantedDestinationPixelTypeH);
142                }
143                if (glob->drawBandUPP) {
144                        DisposeImageCodecMPDrawBandUPP(glob->drawBandUPP);
145                }
146                if (glob->colorSpace) {
147                        CGColorSpaceRelease(glob->colorSpace);
148                }
149                if (glob->ssa) SubDisposeRenderer(glob->ssa);
150                DisposePtr((Ptr)glob);
151        }
152
153        return noErr;
154}
155
156// Component Version Request - Required
157pascal ComponentResult TextSubCodecVersion(TextSubGlobals glob)
158{
159#pragma unused(glob)
160       
161    return kTextSubCodecVersion;
162}
163
164// Component Target Request
165//              Allows another component to "target" you i.e., you call another component whenever
166// you would call yourself (as a result of your component being used by another component)
167pascal ComponentResult TextSubCodecTarget(TextSubGlobals glob, ComponentInstance target)
168{
169        glob->target = target;
170        return noErr;
171}
172
173// Component GetMPWorkFunction Request
174//              Allows your image decompressor component to perform asynchronous decompression
175// in a single MP task by taking advantage of the Base Decompressor. If you implement
176// this selector, your DrawBand function must be MP-safe. MP safety means not
177// calling routines that may move or purge memory and not calling any routines which
178// might cause 68K code to be executed.
179pascal ComponentResult TextSubCodecGetMPWorkFunction(TextSubGlobals glob, ComponentMPWorkFunctionUPP *workFunction, void **refCon)
180{
181        if (NULL == glob->drawBandUPP)
182                glob->drawBandUPP = NewImageCodecMPDrawBandUPP((ImageCodecMPDrawBandProcPtr)TextSubCodecDrawBand);
183               
184        return ImageCodecGetBaseMPWorkFunction(glob->delegateComponent, workFunction, refCon, glob->drawBandUPP, glob);
185}
186
187#pragma mark-
188
189// ImageCodecInitialize
190//              The first function call that your image decompressor component receives from the base image
191// decompressor is always a call to ImageCodecInitialize . In response to this call, your image decompressor
192// component returns an ImageSubCodecDecompressCapabilities structure that specifies its capabilities.
193pascal ComponentResult TextSubCodecInitialize(TextSubGlobals glob, ImageSubCodecDecompressCapabilities *cap)
194{
195#pragma unused(glob)
196
197        // Secifies the size of the ImageSubCodecDecompressRecord structure
198        // and say we can support asyncronous decompression
199        // With the help of the base image decompressor, any image decompressor
200        // that uses only interrupt-safe calls for decompression operations can
201        // support asynchronous decompression.
202        cap->decompressRecordSize = sizeof(TextSubDecompressRecord);
203        cap->canAsync = true;
204       
205        cap->subCodecIsMultiBufferAware = true;
206        cap->subCodecSupportsDecodeSmoothing = true;
207
208        return noErr;
209}
210
211// ImageCodecPreflight
212//              The base image decompressor gets additional information about the capabilities of your image
213// decompressor component by calling ImageCodecPreflight. The base image decompressor uses this
214// information when responding to a call to the ImageCodecPredecompress function,
215// which the ICM makes before decompressing an image. You are required only to provide values for
216// the wantedDestinationPixelSize and wantedDestinationPixelTypes fields and can also modify other
217// fields if necessary.
218pascal ComponentResult TextSubCodecPreflight(TextSubGlobals glob, CodecDecompressParams *p)
219{
220        CodecCapabilities *capabilities = p->capabilities;
221        OSTypePtr         formats = *glob->wantedDestinationPixelTypeH;
222
223        // Fill in formats for wantedDestinationPixelTypeH
224        // Terminate with an OSType value 0  - see IceFloe #7
225        // http://developer.apple.com/quicktime/icefloe/dispatch007.html
226   
227    // we want ARGB because Quartz can use it easily
228    // Todo: add other possible pixel formats Quartz can handle
229    *formats++  = k32RGBAPixelFormat;
230        *formats++      = k32ARGBPixelFormat;
231        *formats++      = 0;
232       
233        // Specify the minimum image band height supported by the component
234        // bandInc specifies a common factor of supported image band heights -
235        // if your component supports only image bands that are an even
236    // multiple of some number of pixels high report this common factor in bandInc
237        capabilities->bandMin = (**p->imageDescription).height;
238        capabilities->bandInc = capabilities->bandMin;
239
240        // Indicate the wanted destination using the wantedDestinationPixelTypeH previously set up
241        capabilities->wantedPixelSize  = 0;     
242   
243        p->wantedDestinationPixelTypes = glob->wantedDestinationPixelTypeH;
244
245        // Specify the number of pixels the image must be extended in width and height if
246        // the component cannot accommodate the image at its given width and height
247        capabilities->extendWidth = 0;
248        capabilities->extendHeight = 0;
249       
250        capabilities->flags |= codecCanAsync | codecCanAsyncWhen | codecCanScale;
251        capabilities->flags2 |= codecDrawsHigherQualityScaled;   
252       
253        if (!glob->ssa) {
254                if ((**p->imageDescription).cType == kSubFormatSSA) {
255                        long count;
256                        glob->translateSRT = false;
257                       
258                        CountImageDescriptionExtensionType(p->imageDescription,kSubFormatSSA,&count);
259                        if (count == 1) {
260                                Handle ssaheader;
261                                GetImageDescriptionExtension(p->imageDescription,&ssaheader,kSubFormatSSA,1);
262                               
263                                glob->ssa = SubInitForSSA(*ssaheader, GetHandleSize(ssaheader), (**p->imageDescription).width, (**p->imageDescription).height);
264                        } 
265                } 
266               
267                if (!glob->ssa) glob->ssa = SubInitNonSSA((**p->imageDescription).width,(**p->imageDescription).height);
268                               
269                glob->colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
270        }
271       
272        return noErr;
273}
274
275// ImageCodecBeginBand
276//              The ImageCodecBeginBand function allows your image decompressor component to save information about
277// a band before decompressing it. This function is never called at interrupt time. The base image decompressor
278// preserves any changes your component makes to any of the fields in the ImageSubCodecDecompressRecord
279// or CodecDecompressParams structures. If your component supports asynchronous scheduled decompression, it
280// may receive more than one ImageCodecBeginBand call before receiving an ImageCodecDrawBand call.
281pascal ComponentResult TextSubCodecBeginBand(TextSubGlobals glob, CodecDecompressParams *p, ImageSubCodecDecompressRecord *drp, long flags)
282{
283        TextSubDecompressRecord *myDrp = (TextSubDecompressRecord *)drp->userDecompressRecord;
284       
285        // Let base codec know that all our frames are key frames (a.k.a., sync samples)
286        // This allows the base codec to perform frame dropping on our behalf if needed
287    drp->frameType = kCodecFrameTypeKey;
288
289        myDrp->pixelFormat = p->dstPixMap.pixelFormat;
290        myDrp->width = p->dstRect.right - p->dstRect.left;
291        myDrp->height = p->dstRect.bottom - p->dstRect.top;
292        myDrp->depth = (**p->imageDescription).depth;
293    myDrp->dataSize = p->bufferSize;
294       
295        return noErr;
296}
297
298// ImageCodecDrawBand
299//              The base image decompressor calls your image decompressor component's ImageCodecDrawBand function
300// to decompress a band or frame. Your component must implement this function. If the ImageSubCodecDecompressRecord
301// structure specifies a progress function or data-loading function, the base image decompressor will never call ImageCodecDrawBand
302// at interrupt time. If the ImageSubCodecDecompressRecord structure specifies a progress function, the base image decompressor
303// handles codecProgressOpen and codecProgressClose calls, and your image decompressor component must not implement these functions.
304// If not, the base image decompressor may call the ImageCodecDrawBand function at interrupt time.
305// When the base image decompressor calls your ImageCodecDrawBand function, your component must perform the decompression specified
306// by the fields of the ImageSubCodecDecompressRecord structure. The structure includes any changes your component made to it
307// when performing the ImageCodecBeginBand function. If your component supports asynchronous scheduled decompression,
308// it may receive more than one ImageCodecBeginBand call before receiving an ImageCodecDrawBand call.
309pascal ComponentResult TextSubCodecDrawBand(TextSubGlobals glob, ImageSubCodecDecompressRecord *drp)
310{
311        TextSubDecompressRecord *myDrp = (TextSubDecompressRecord *)drp->userDecompressRecord;
312        CGImageAlphaInfo alphaFormat = (myDrp->pixelFormat == k32ARGBPixelFormat) ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaPremultipliedLast;
313
314    CGContextRef c = CGBitmapContextCreate(drp->baseAddr, myDrp->width, myDrp->height,
315                                                                                   8, drp->rowBytes,  glob->colorSpace,
316                                                                                   alphaFormat);
317       
318        CGContextClearRect(c, CGRectMake(0,0, myDrp->width, myDrp->height));
319       
320        CFMutableStringRef buf;
321       
322        if (drp->codecData[0] == '\n' && myDrp->dataSize == 1) goto leave; // skip empty packets
323       
324        if (glob->translateSRT) {
325                buf = CFStringCreateMutableWithBytes(NULL, drp->codecData, myDrp->dataSize, kCFStringEncodingUTF8);
326                if (!buf) goto leave;
327                CFStringFindAndReplace(buf, CFSTR("<i>"),  CFSTR("{\\i1}"), CFRangeMake(0,CFStringGetLength(buf)), 0);
328                CFStringFindAndReplace(buf, CFSTR("</i>"), CFSTR("{\\i0}"), CFRangeMake(0,CFStringGetLength(buf)), 0);
329                CFStringFindAndReplace(buf, CFSTR("<"),    CFSTR("{"),      CFRangeMake(0,CFStringGetLength(buf)), 0);
330                CFStringFindAndReplace(buf, CFSTR(">"),    CFSTR("}"),      CFRangeMake(0,CFStringGetLength(buf)), 0);
331        } else {
332                buf = (CFMutableStringRef)CFStringCreateWithBytes(NULL, (UInt8*)drp->codecData, myDrp->dataSize, kCFStringEncodingUTF8, false);
333                if (!buf) goto leave;
334        }
335       
336        SubRenderPacket(glob->ssa,c,buf,myDrp->width,myDrp->height);
337       
338#if 0
339        {
340                CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, "/subtitle.pdf", strlen("/subtitle.pdf"), 0);
341                CGRect rect = CGRectMake(0,0, myDrp->width, myDrp->height);
342                CGContextRef pc = CGPDFContextCreateWithURL(url, &rect, NULL);
343               
344                CGPDFContextBeginPage(pc, NULL);
345                SubRenderPacket(glob->ssa,pc,buf,myDrp->width,myDrp->height);
346                CGPDFContextEndPage(pc);
347                CGContextRelease(pc);
348                CFRelease(url);
349        }
350#endif
351       
352        CFRelease(buf);
353       
354leave:
355        CGContextRelease(c);
356        return noErr;
357}
358
359// ImageCodecEndBand
360//              The ImageCodecEndBand function notifies your image decompressor component that decompression of a band has finished or
361// that it was terminated by the Image Compression Manager. Your image decompressor component is not required to implement
362// the ImageCodecEndBand function. The base image decompressor may call the ImageCodecEndBand function at interrupt time.
363// After your image decompressor component handles an ImageCodecEndBand call, it can perform any tasks that are required
364// when decompression is finished, such as disposing of data structures that are no longer needed. Because this function
365// can be called at interrupt time, your component cannot use this function to dispose of data structures; this
366// must occur after handling the function. The value of the result parameter should be set to noErr if the band or frame was
367// drawn successfully. If it is any other value, the band or frame was not drawn.
368pascal ComponentResult TextSubCodecEndBand(TextSubGlobals glob, ImageSubCodecDecompressRecord *drp, OSErr result, long flags)
369{
370#pragma unused(glob, drp,result, flags)
371       
372        return noErr;
373}
374
375// ImageCodecQueueStarting
376//              If your component supports asynchronous scheduled decompression, the base image decompressor calls your image decompressor component's
377// ImageCodecQueueStarting function before decompressing the frames in the queue. Your component is not required to implement this function.
378// It can implement the function if it needs to perform any tasks at this time, such as locking data structures.
379// The base image decompressor never calls the ImageCodecQueueStarting function at interrupt time.
380pascal ComponentResult TextSubCodecQueueStarting(TextSubGlobals glob)
381{
382#pragma unused(glob)
383       
384        return noErr;
385}
386
387// ImageCodecQueueStopping
388//               If your image decompressor component supports asynchronous scheduled decompression, the ImageCodecQueueStopping function notifies
389// your component that the frames in the queue have been decompressed. Your component is not required to implement this function.
390// After your image decompressor component handles an ImageCodecQueueStopping call, it can perform any tasks that are required when decompression
391// of the frames is finished, such as disposing of data structures that are no longer needed.
392// The base image decompressor never calls the ImageCodecQueueStopping function at interrupt time.
393pascal ComponentResult TextSubCodecQueueStopping(TextSubGlobals glob)
394{
395#pragma unused(glob)
396       
397        return noErr;
398}
399
400// ImageCodecGetCompressedImageSize
401//              Your component receives the ImageCodecGetCompressedImageSize request whenever an application calls the ICM's GetCompressedImageSize function.
402// You can use the ImageCodecGetCompressedImageSize function when you are extracting a single image from a sequence; therefore, you don't have an
403// image description structure and don't know the exact size of one frame. In this case, the Image Compression Manager calls the component to determine
404// the size of the data. Your component should return a long integer indicating the number of bytes of data in the compressed image. You may want to store
405// the image size somewhere in the image description structure, so that you can respond to this request quickly. Only decompressors receive this request.
406pascal ComponentResult TextSubCodecGetCompressedImageSize(TextSubGlobals glob, ImageDescriptionHandle desc, Ptr data, long dataSize, ICMDataProcRecordPtr dataProc, long *size)
407{
408#pragma unused(glob,dataProc,desc)
409        if (size == NULL) 
410                return paramErr;
411
412//      *size = EndianU32_BtoN(dataSize);
413
414        return noErr;
415}
416
417// ImageCodecGetCodecInfo
418//              Your component receives the ImageCodecGetCodecInfo request whenever an application calls the Image Compression Manager's GetCodecInfo function.
419// Your component should return a formatted compressor information structure defining its capabilities.
420// Both compressors and decompressors may receive this request.
421pascal ComponentResult TextSubCodecGetCodecInfo(TextSubGlobals glob, CodecInfo *info)
422{
423        OSErr err = noErr;
424        ComponentDescription desc;
425        short resid;
426       
427        GetComponentInfo((Component)glob->self, &desc, 0, 0, 0);
428       
429        if (desc.componentSubType == kSubFormatSSA)
430                resid = kSSASubCodecResourceID;
431        else
432                resid = kTextSubCodecResourceID;
433
434        if (info == NULL) {
435                err = paramErr;
436        } else {
437                CodecInfo **tempCodecInfo;
438
439                err = GetComponentResource((Component)glob->self, codecInfoResourceType, resid, (Handle *)&tempCodecInfo);
440                if (err == noErr) {
441                        *info = **tempCodecInfo;
442                        DisposeHandle((Handle)tempCodecInfo);
443                }
444        }
445
446        return err;
447}
Note: See TracBrowser for help on using the repository browser.