source: branches/perian-1.1/VobSubCodec.c @ 887

Revision 887, 16.0 KB checked in by astrange, 7 years ago (diff)

Merge trunk to 1.1 branch.

  • Property svn:executable set to *
Line 
1
2#include <Carbon/Carbon.h>
3#include <QuickTime/QuickTime.h>
4#include "VobSubCodec.h"
5#include "Codecprintf.h"
6#include <zlib.h>
7#include "avcodec.h"
8#include "intreadwrite.h"
9
10// Constants
11const UInt8 kNumPixelFormatsSupportedVobSub = 1;
12
13// Data structures
14typedef struct  {
15        ComponentInstance       self;
16        ComponentInstance       delegateComponent;
17        ComponentInstance       target;
18        OSType**                wantedDestinationPixelTypeH;
19        ImageCodecMPDrawBandUPP drawBandUPP;
20       
21        UInt32                  palette[16];
22       
23        int                     compressed;
24        uint8_t                 *codecData;
25        unsigned int            bufferSize;
26       
27        AVCodec                 *avCodec;
28        AVCodecContext          *avContext;
29        AVSubtitle              subtitle;
30} VobSubCodecGlobalsRecord, *VobSubCodecGlobals;
31
32typedef struct {
33        long            width;
34        long            height;
35        long            bufferSize;
36        char            decoded;
37} VobSubDecompressRecord;
38
39typedef struct {
40        // color format is 32-bit ARGB
41        UInt32  pixelColor[16];
42        UInt32  duration;
43} PacketControlData;
44
45// Setup required for ComponentDispatchHelper.c
46#define IMAGECODEC_BASENAME()           VobSubCodec
47#define IMAGECODEC_GLOBALS()            VobSubCodecGlobals storage
48
49#define CALLCOMPONENT_BASENAME()        IMAGECODEC_BASENAME()
50#define CALLCOMPONENT_GLOBALS()         IMAGECODEC_GLOBALS()
51
52#define COMPONENT_UPP_PREFIX()          uppImageCodec
53#define COMPONENT_DISPATCH_FILE         "VobSubCodecDispatch.h"
54#define COMPONENT_SELECT_PREFIX()       kImageCodec
55
56#define GET_DELEGATE_COMPONENT()        (storage->delegateComponent)
57
58#include <CoreServices/Components.k.h>
59#include <QuickTime/ImageCodec.k.h>
60#include <QuickTime/ComponentDispatchHelper.c>
61
62
63// dest must be at least as large as src
64void ExtractVobSubPacket(UInt8 *dest, UInt8 *framedSrc, int srcSize);
65static ComponentResult ReadPacketControls(UInt8 *packet, UInt32 palette[16], PacketControlData *controlDataOut);
66extern void initLib();
67
68ComponentResult VobSubCodecOpen(VobSubCodecGlobals glob, ComponentInstance self)
69{
70        ComponentResult err;
71        // Allocate memory for our globals, set them up and inform the component manager that we've done so
72        glob = (VobSubCodecGlobals)NewPtrClear(sizeof(VobSubCodecGlobalsRecord));
73        if (err = MemError()) goto bail;
74
75        SetComponentInstanceStorage(self, (Handle)glob);
76
77        glob->self = self;
78        glob->target = self;
79        glob->wantedDestinationPixelTypeH = (OSType **)NewHandle(sizeof(OSType) * 2);
80        if (err = MemError()) goto bail;
81        glob->drawBandUPP = NULL;
82       
83        // Open and target an instance of the base decompressor as we delegate
84        // most of our calls to the base decompressor instance
85        err = OpenADefaultComponent(decompressorComponentType, kBaseCodecType, &glob->delegateComponent);
86        if (err) goto bail;
87
88        ComponentSetTarget(glob->delegateComponent, self);
89
90bail:
91        return err;
92}
93
94ComponentResult VobSubCodecClose(VobSubCodecGlobals glob, ComponentInstance self)
95{
96        int i;
97       
98        // Make sure to close the base component and dealocate our storage
99        if (glob) {
100                if (glob->delegateComponent) {
101                        CloseComponent(glob->delegateComponent);
102                }
103                if (glob->wantedDestinationPixelTypeH) {
104                        DisposeHandle((Handle)glob->wantedDestinationPixelTypeH);
105                }
106                if (glob->drawBandUPP) {
107                        DisposeImageCodecMPDrawBandUPP(glob->drawBandUPP);
108                }
109                if (glob->codecData) {
110                        av_free(glob->codecData);
111                }
112                if (glob->avCodec) {
113                        avcodec_close(glob->avContext);
114                }
115                if (glob->avContext) {
116                        av_free(glob->avContext);
117                }
118                if (glob->subtitle.rects) {
119                        for (i = 0; i < glob->subtitle.num_rects; i++) {
120                                av_free(glob->subtitle.rects[i].rgba_palette);
121                                av_free(glob->subtitle.rects[i].bitmap);
122                        }
123                        av_free(glob->subtitle.rects);
124                }
125
126                DisposePtr((Ptr)glob);
127        }
128
129        return noErr;
130}
131
132ComponentResult VobSubCodecVersion(VobSubCodecGlobals glob)
133{       
134        return kVobSubCodecVersion;
135}
136
137ComponentResult VobSubCodecTarget(VobSubCodecGlobals glob, ComponentInstance target)
138{
139        glob->target = target;
140        return noErr;
141}
142
143ComponentResult VobSubCodecGetMPWorkFunction(VobSubCodecGlobals glob, ComponentMPWorkFunctionUPP *workFunction, void **refCon)
144{
145        if (glob->drawBandUPP == NULL)
146                glob->drawBandUPP = NewImageCodecMPDrawBandUPP((ImageCodecMPDrawBandProcPtr)VobSubCodecDrawBand);
147       
148        return ImageCodecGetBaseMPWorkFunction(glob->delegateComponent, workFunction, refCon, glob->drawBandUPP, glob);
149}
150
151ComponentResult VobSubCodecInitialize(VobSubCodecGlobals glob, ImageSubCodecDecompressCapabilities *cap)
152{
153        cap->decompressRecordSize = sizeof(VobSubDecompressRecord);
154        cap->canAsync = true;
155        cap->baseCodecShouldCallDecodeBandForAllFrames = true;
156       
157        if(cap->recordSize > offsetof(ImageSubCodecDecompressCapabilities, baseCodecShouldCallDecodeBandForAllFrames)) 
158                cap->subCodecIsMultiBufferAware = true;
159
160        return noErr;
161}
162
163static ComponentResult SetupColorPalette(VobSubCodecGlobals glob, ImageDescriptionHandle imageDescription) {
164        OSErr err = noErr;
165        Handle descExtension = NewHandle(0);
166       
167        err = GetImageDescriptionExtension(imageDescription, &descExtension, kVobSubIdxExtension, 1);
168        if (err) goto bail;
169       
170        char *string = (char *) *descExtension;
171       
172        char *palette = strnstr(string, "palette:", GetHandleSize(descExtension));
173       
174        if (palette != NULL) {
175                sscanf(palette, "palette: %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx, %lx", 
176                           &glob->palette[ 0], &glob->palette[ 1], &glob->palette[ 2], &glob->palette[ 3], 
177                           &glob->palette[ 4], &glob->palette[ 5], &glob->palette[ 6], &glob->palette[ 7], 
178                           &glob->palette[ 8], &glob->palette[ 9], &glob->palette[10], &glob->palette[11], 
179                           &glob->palette[12], &glob->palette[13], &glob->palette[14], &glob->palette[15]);
180        }
181       
182bail:
183        DisposeHandle(descExtension);
184        return err;
185}
186
187ComponentResult VobSubCodecPreflight(VobSubCodecGlobals glob, CodecDecompressParams *p)
188{
189        CodecCapabilities *capabilities = p->capabilities;
190        OSTypePtr         formats = *glob->wantedDestinationPixelTypeH;
191
192        // Specify the minimum image band height supported by the component
193        // bandInc specifies a common factor of supported image band heights
194        capabilities->bandMin = (**p->imageDescription).height;
195        capabilities->bandInc = capabilities->bandMin;
196
197        // Indicate the wanted destination using the wantedDestinationPixelTypeH previously set up
198        capabilities->wantedPixelSize  = 0;     
199       
200        // we want 4:4:4:4 ARGB
201        *formats++ = k32ARGBPixelFormat;
202        *formats++ = 0;
203       
204        p->wantedDestinationPixelTypes = glob->wantedDestinationPixelTypeH;
205
206        // Specify the number of pixels the image must be extended in width and height if
207        // the component cannot accommodate the image at its given width and height
208        capabilities->extendWidth = 0;
209        capabilities->extendHeight = 0;
210       
211        // get the color palette info from the image description
212        SetupColorPalette(glob, p->imageDescription);
213
214        if (isImageDescriptionExtensionPresent(p->imageDescription, kMKVCompressionExtension))
215                glob->compressed = 1;
216       
217        if (!glob->avCodec) {
218                init_FFmpeg();
219               
220                glob->avCodec = avcodec_find_decoder(CODEC_ID_DVD_SUBTITLE);
221                glob->avContext = avcodec_alloc_context();
222               
223                if (avcodec_open(glob->avContext, glob->avCodec)) {
224                        Codecprintf(NULL, "Error opening DVD subtitle decoder\n");
225                        return codecErr;
226                }
227        }
228       
229        return noErr;
230}
231
232ComponentResult VobSubCodecBeginBand(VobSubCodecGlobals glob, CodecDecompressParams *p, ImageSubCodecDecompressRecord *drp, long flags)
233{
234        VobSubDecompressRecord *myDrp = (VobSubDecompressRecord *)drp->userDecompressRecord;
235
236        // Let base codec know that all our frames are keyframes
237        drp->frameType = kCodecFrameTypeKey;
238
239        myDrp->width = (**p->imageDescription).width;
240        myDrp->height = (**p->imageDescription).height;
241        myDrp->bufferSize = p->bufferSize;
242        myDrp->decoded = p->frameTime ? (0 != (p->frameTime->flags & icmFrameAlreadyDecoded)) : false;
243       
244        return noErr;
245}
246
247void DecompressZlib(VobSubCodecGlobals glob, uint8_t *data, long *bufferSize)
248{
249        ComponentResult err = noErr;
250        z_stream strm;
251        strm.zalloc = Z_NULL;
252        strm.zfree = Z_NULL;
253        strm.opaque = Z_NULL;
254        strm.avail_in = 0;
255        strm.next_in = Z_NULL;
256        err = inflateInit(&strm);
257        if (err != Z_OK) return;
258       
259        strm.avail_in = *bufferSize;
260        strm.next_in = data;
261       
262        // first, get the size of the decompressed data
263        strm.avail_out = 2;
264        strm.next_out = glob->codecData;
265       
266        err = inflate(&strm, Z_SYNC_FLUSH);
267        if (err < Z_OK) goto bail;
268        if (strm.avail_out != 0) goto bail;
269       
270        // reallocate our buffer to be big enough to store the decompressed packet
271        *bufferSize = AV_RB16(glob->codecData);
272        glob->codecData = av_fast_realloc(glob->codecData, &glob->bufferSize, *bufferSize);
273       
274        // then decompress the rest of it
275        strm.avail_out = glob->bufferSize - 2;
276        strm.next_out = glob->codecData + 2;
277       
278        inflate(&strm, Z_SYNC_FLUSH);
279bail:
280        inflateEnd(&strm);
281}
282
283ComponentResult VobSubCodecDecodeBand(VobSubCodecGlobals glob, ImageSubCodecDecompressRecord *drp, unsigned long flags)
284{
285        VobSubDecompressRecord *myDrp = (VobSubDecompressRecord *)drp->userDecompressRecord;
286        UInt8 *data = (UInt8 *) drp->codecData;
287        int ret, got_sub;
288       
289        if (glob->codecData == NULL) {
290                glob->codecData = malloc(myDrp->bufferSize + 2);
291                glob->bufferSize = myDrp->bufferSize + 2;
292        }
293       
294        // make sure we have enough space to store the packet
295        glob->codecData = av_fast_realloc(glob->codecData, &glob->bufferSize, myDrp->bufferSize + 2);
296       
297        if (glob->compressed) {
298                DecompressZlib(glob, data, &myDrp->bufferSize);
299               
300        // the header of a spu PS packet starts 0x000001bd
301        // if it's raw spu data, the 1st 2 bytes are the length of the data
302        } else if (data[0] + data[1] == 0) {
303                // remove the MPEG framing
304                ExtractVobSubPacket(glob->codecData, data, myDrp->bufferSize);
305        } else {
306                memcpy(glob->codecData, drp->codecData, myDrp->bufferSize);
307        }
308       
309        ret = avcodec_decode_subtitle(glob->avContext, &glob->subtitle, &got_sub, glob->codecData, myDrp->bufferSize);
310       
311        if (ret < 0 || !got_sub) {
312                Codecprintf(NULL, "Error decoding DVD subtitle %d / %ld\n", ret, myDrp->bufferSize);
313                return codecErr;
314        }
315       
316        myDrp->decoded = true;
317       
318        return noErr;
319}
320
321ComponentResult VobSubCodecDrawBand(VobSubCodecGlobals glob, ImageSubCodecDecompressRecord *drp)
322{
323        OSErr err = noErr;
324        VobSubDecompressRecord *myDrp = (VobSubDecompressRecord *)drp->userDecompressRecord;
325        PacketControlData controlData;
326        uint8_t *data = glob->codecData;
327        unsigned int i, j, x, y;
328        int usePalette = 0;
329       
330        if(!myDrp->decoded)
331                err = VobSubCodecDecodeBand(glob, drp, 0);
332        if (err) return err;
333       
334        // clear the buffer to pure transparent
335        memset(drp->baseAddr, 0, myDrp->height * drp->rowBytes);
336       
337        err = ReadPacketControls(data, glob->palette, &controlData);
338        if (err == noErr)
339                usePalette = true;
340       
341        for (i = 0; i < glob->subtitle.num_rects; i++) {
342                AVSubtitleRect *rect = &glob->subtitle.rects[i];
343                uint8_t *line = (uint8_t *)drp->baseAddr + drp->rowBytes * rect->y + rect->x*4;
344                uint8_t *sub = rect->bitmap;
345                unsigned int w = FFMIN(rect->w, myDrp->width  - rect->x);
346                unsigned int h = FFMIN(rect->h, myDrp->height - rect->y);
347               
348                if (usePalette) {
349                        for (j = 0; j < 4; j++)
350                                rect->rgba_palette[j] = controlData.pixelColor[j];
351                }
352               
353                for (y = 0; y < h; y++) {
354                        uint32_t *pixel = (uint32_t *) line;
355                       
356                        for (x = 0; x < w; x++)
357                                pixel[x] = rect->rgba_palette[sub[x]];
358                       
359                        line += drp->rowBytes;
360                        sub += rect->linesize;
361                }
362        }
363       
364        return err;
365}
366
367ComponentResult VobSubCodecEndBand(VobSubCodecGlobals glob, ImageSubCodecDecompressRecord *drp, OSErr result, long flags)
368{
369        return noErr;
370}
371
372ComponentResult VobSubCodecQueueStarting(VobSubCodecGlobals glob)
373{
374        return noErr;
375}
376
377ComponentResult VobSubCodecQueueStopping(VobSubCodecGlobals glob)
378{
379        return noErr;
380}
381
382ComponentResult VobSubCodecGetCompressedImageSize(VobSubCodecGlobals glob, ImageDescriptionHandle desc, Ptr data, long dataSize, ICMDataProcRecordPtr dataProc, long *size)
383{
384        if (size == NULL) 
385                return paramErr;
386       
387        return unimpErr;
388}
389
390ComponentResult VobSubCodecGetCodecInfo(VobSubCodecGlobals glob, CodecInfo *info)
391{
392        OSErr err = noErr;
393        if (info == NULL) {
394                err = paramErr;
395        } else {
396                CodecInfo **tempCodecInfo;
397
398                err = GetComponentResource((Component)glob->self, codecInfoResourceType, kVobSubCodecResource, (Handle *)&tempCodecInfo);
399                if (err == noErr) {
400                        *info = **tempCodecInfo;
401                        DisposeHandle((Handle)tempCodecInfo);
402                }
403        }
404
405        return err;
406}
407
408void ExtractVobSubPacket(UInt8 *dest, UInt8 *framedSrc, int srcSize) {
409        int copiedBytes = 0;
410        UInt8 *currentPacket = framedSrc;
411       
412        while (currentPacket - framedSrc < srcSize) {
413                // 3-byte start code: 0x00 00 01
414                if (currentPacket[0] + currentPacket[1] != 0 || currentPacket[2] != 1) {
415                        Codecprintf(NULL, "VobSub Codec: !! Unknown header: %02x %02x %02x\n", currentPacket[0], currentPacket[1], currentPacket[2]);
416                        return;
417                }
418               
419                int packet_length;
420               
421                switch (currentPacket[3]) {
422                        case 0xba:
423                                // discard PS packets; nothing in them we're interested in
424                                // here, packet_length is the additional stuffing
425                                packet_length = currentPacket[13] & 0x7;
426                               
427                                currentPacket += 14 + packet_length;
428                                break;
429                               
430                        case 0xbe:
431                        case 0xbf:
432                                // skip padding and navigation data
433                                // (navigation shouldn't be present anyway)
434                                packet_length = currentPacket[4];
435                                packet_length <<= 8;
436                                packet_length += currentPacket[5];
437                               
438                                currentPacket += 6 + packet_length;
439                                break;
440                               
441                        case 0xbd:
442                                // a private stream packet, contains subtitle data
443                                packet_length = currentPacket[4];
444                                packet_length <<= 8;
445                                packet_length += currentPacket[5];
446                               
447                                int header_data_length = currentPacket[8];
448                               
449                                memcpy(&dest[copiedBytes], 
450                                           // header's 9 bytes + extension, we don't want 1st byte of packet
451                                           &currentPacket[9 + header_data_length + 1], 
452                                           // we don't want the 1-byte stream ID, or the header
453                                           packet_length - 1 - (header_data_length + 3));
454                               
455                                copiedBytes += packet_length - 1 - (header_data_length + 3);
456                                currentPacket += packet_length + 6;
457                                break;
458                               
459                        default:
460                                // unknown packet, probably video, return for now
461                                Codecprintf(NULL, "VobSubCodec - Unknown packet type %x, aborting\n", (int)currentPacket[3]);
462                                return;
463                } // switch (currentPacket[3])
464        } // while (currentPacket - framedSrc < srcSize)
465}
466
467ComponentResult ReadPacketControls(UInt8 *packet, UInt32 palette[16], PacketControlData *controlDataOut) {
468        // to set whether the key sequences 0x03 - 0x06 have been seen
469        UInt16 controlSeqSeen = 0;
470        int i = 0;
471        Boolean loop = TRUE;
472        int controlOffset = (packet[2] << 8) + packet[3] + 4;
473        uint8_t *controlSeq = packet + controlOffset;
474       
475        memset(controlDataOut, 0, sizeof(PacketControlData));
476       
477        while (loop) {
478                switch (controlSeq[i]) {
479                        case 0x00:
480                                // subpicture identifier, we don't care
481                                i++;
482                                break;
483                               
484                        case 0x01:
485                                // start displaying, we don't care
486                                i++;
487                                break;
488                               
489                        case 0x03:
490                                // palette info
491                                controlDataOut->pixelColor[3] += palette[controlSeq[i+1] >> 4 ];
492                                controlDataOut->pixelColor[2] += palette[controlSeq[i+1] & 0xf];
493                                controlDataOut->pixelColor[1] += palette[controlSeq[i+2] >> 4 ];
494                                controlDataOut->pixelColor[0] += palette[controlSeq[i+2] & 0xf];
495                               
496                                i += 3;
497                                controlSeqSeen |= 0x0f;
498                                break;
499                               
500                        case 0x04:
501                                // alpha info
502                                controlDataOut->pixelColor[3] += (controlSeq[i + 1] & 0xf0) << 20;
503                                controlDataOut->pixelColor[2] += (controlSeq[i + 1] & 0x0f) << 24;
504                                controlDataOut->pixelColor[1] += (controlSeq[i + 2] & 0xf0) << 20;
505                                controlDataOut->pixelColor[0] += (controlSeq[i + 2] & 0x0f) << 24;
506                               
507                                // double the nibble
508                                controlDataOut->pixelColor[3] += (controlSeq[i + 1] & 0xf0) << 24;
509                                controlDataOut->pixelColor[2] += (controlSeq[i + 1] & 0x0f) << 28;
510                                controlDataOut->pixelColor[1] += (controlSeq[i + 2] & 0xf0) << 24;
511                                controlDataOut->pixelColor[0] += (controlSeq[i + 2] & 0x0f) << 28;
512                               
513                                i += 3;
514                                controlSeqSeen |= 0xf0;
515                                break;
516                               
517                        case 0x05:
518                                // coordinates of image, ffmpeg takes care of this
519                                i += 7;
520                                break;
521                               
522                        case 0x06:
523                                // offset of the first graphic line, and second, ffmpeg takes care of this
524                                i += 5;
525                                break;
526                               
527                        case 0xff:
528                                // end of control sequence
529                                loop = FALSE;
530                                break;
531                               
532                        default:
533                                Codecprintf(NULL, " !! Unknown control sequence 0x%02x  aborting (offset %x)\n", controlSeq[i], i);
534                                loop = FALSE;
535                                break;
536                }
537        }
538       
539        // force fully transparent to transparent black; needed? for graphicsModePreBlackAlpha
540        for (i = 0; i < 4; i++) {
541                if ((controlDataOut->pixelColor[i] & 0xff000000) == 0)
542                        controlDataOut->pixelColor[i] = 0;
543        }
544       
545        if (controlSeqSeen != 0xff)
546                return -1;
547        return noErr;
548}
Note: See TracBrowser for help on using the repository browser.