OSDN Git Service

Remove the set cpu count option as it doesn't do anything now
[handbrake-jp/handbrake-jp-git.git] / libhb / dectx3gsub.c
1 /* 
2    This file is part of the HandBrake source code.
3    Homepage: <http://handbrake.fr/>.
4    It may be used under the terms of the GNU General Public License. */
5
6 /*
7  * Converts TX3G subtitles to UTF-8 subtitles with limited HTML-style markup (<b>, <i>, <u>).
8  * 
9  * TX3G == MPEG 4, Part 17 (ISO/IEC 14496-17) == 3GPP Timed Text (26.245)
10  * A full reference to the format can be found here:
11  * http://www.3gpp.org/ftp/Specs/html-info/26245.htm
12  * 
13  * @author David Foster (davidfstr)
14  */
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include "hb.h"
19
20 typedef enum {
21     BOLD        = 0x1,
22     ITALIC      = 0x2,
23     UNDERLINE   = 0x4
24 } FaceStyleFlag;
25
26 #define NUM_FACE_STYLE_FLAGS 3
27 #define MAX_OPEN_TAG_SIZE 3     // "<b>"
28 #define MAX_CLOSE_TAG_SIZE 4    // "</b>"
29
30 typedef struct {
31     uint16_t startChar;       // NOTE: indices in terms of *character* (not: byte) positions
32     uint16_t endChar;
33     uint16_t fontID;
34     uint8_t faceStyleFlags;   // FaceStyleFlag
35     uint8_t fontSize;
36     uint32_t textColorRGBA;
37 } StyleRecord;
38
39 // NOTE: None of these macros check for buffer overflow
40 #define READ_U8()       *pos;                                                       pos += 1;
41 #define READ_U16()      (pos[0] << 8) | pos[1];                                     pos += 2;
42 #define READ_U32()      (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | pos[3];   pos += 4;
43 #define READ_ARRAY(n)   pos;                                                        pos += n;
44 #define SKIP_ARRAY(n)   pos += n;
45
46 #define WRITE_CHAR(c)       {dst[0]=c;                                              dst += 1;}
47 #define WRITE_START_TAG(c)  {dst[0]='<'; dst[1]=c;   dst[2]='>';                    dst += 3;}
48 #define WRITE_END_TAG(c)    {dst[0]='<'; dst[1]='/'; dst[2]=c; dst[3]='>';          dst += 4;}
49
50 #define FOURCC(str)    ((((uint32_t) str[0]) << 24) | \
51                         (((uint32_t) str[1]) << 16) | \
52                         (((uint32_t) str[2]) << 8) | \
53                         (((uint32_t) str[3]) << 0))
54 #define IS_10xxxxxx(c) ((c & 0xC0) == 0x80)
55
56 static hb_buffer_t *tx3g_decode_to_utf8( hb_buffer_t *in )
57 {
58     uint8_t *pos = in->data;
59     uint8_t *end = in->data + in->size;
60     
61     uint16_t numStyleRecords = 0;
62     
63     uint8_t *startStyle;
64     uint8_t *endStyle;
65     
66     /*
67      * Parse the packet as a TX3G TextSample.
68      * 
69      * Look for a single StyleBox ('styl') and read all contained StyleRecords.
70      * Ignore all other box types.
71      * 
72      * NOTE: Buffer overflows on read are not checked.
73      */
74     uint16_t textLength = READ_U16();
75     uint8_t *text = READ_ARRAY(textLength);
76     startStyle = calloc( textLength, 1 );
77     endStyle = calloc( textLength, 1 );
78     while ( pos < end ) {
79         /*
80          * Read TextSampleModifierBox
81          */
82         uint32_t size = READ_U32();
83         if ( size == 0 ) {
84             size = pos - end;   // extends to end of packet
85         }
86         if ( size == 1 ) {
87             hb_log( "dectx3gsub: TextSampleModifierBox has unsupported large size" );
88             break;
89         }
90         uint32_t type = READ_U32();
91         if ( type == FOURCC("uuid") ) {
92             hb_log( "dectx3gsub: TextSampleModifierBox has unsupported extended type" );
93             break;
94         }
95         
96         if ( type == FOURCC("styl") ) {
97             // Found a StyleBox. Parse the contained StyleRecords
98             
99             if ( numStyleRecords != 0 ) {
100                 hb_log( "dectx3gsub: found additional StyleBoxes on subtitle; skipping" );
101                 SKIP_ARRAY(size);
102                 continue;
103             }
104             
105             numStyleRecords = READ_U16();
106             
107             int i;
108             for (i=0; i<numStyleRecords; i++) {
109                 StyleRecord curRecord;
110                 curRecord.startChar         = READ_U16();
111                 curRecord.endChar           = READ_U16();
112                 curRecord.fontID            = READ_U16();
113                 curRecord.faceStyleFlags    = READ_U8();
114                 curRecord.fontSize          = READ_U8();
115                 curRecord.textColorRGBA     = READ_U32();
116                 
117                 startStyle[curRecord.startChar] |= curRecord.faceStyleFlags;
118                 endStyle[curRecord.endChar]     |= curRecord.faceStyleFlags;
119             }
120         } else {
121             // Found some other kind of TextSampleModifierBox. Skip it.
122             SKIP_ARRAY(size);
123         }
124     }
125     
126     /*
127      * Copy text to output buffer, and add HTML markup for the style records
128      */
129     int maxOutputSize = textLength + (numStyleRecords * NUM_FACE_STYLE_FLAGS * (MAX_OPEN_TAG_SIZE + MAX_CLOSE_TAG_SIZE));
130     hb_buffer_t *out = hb_buffer_init( maxOutputSize );
131     if ( out == NULL )
132         goto fail;
133     uint8_t *dst = out->data;
134     int charIndex = 0;
135     for ( pos = text, end = text + textLength; pos < end; pos++ ) {
136         if (IS_10xxxxxx(*pos)) {
137             // Is a non-first byte of a multi-byte UTF-8 character
138             WRITE_CHAR(*pos);
139             continue;   // ...without incrementing 'charIndex'
140         }
141         
142         uint8_t plusStyles = startStyle[charIndex];
143         uint8_t minusStyles = endStyle[charIndex];
144         
145         if (minusStyles & UNDERLINE)
146             WRITE_END_TAG('u');
147         if (minusStyles & ITALIC)
148             WRITE_END_TAG('i');
149         if (minusStyles & BOLD)
150             WRITE_END_TAG('b');
151         
152         if (plusStyles & BOLD)
153             WRITE_START_TAG('b');
154         if (plusStyles & ITALIC)
155             WRITE_START_TAG('i');
156         if (plusStyles & UNDERLINE)
157             WRITE_START_TAG('u');
158         
159         WRITE_CHAR(*pos);
160         charIndex++;
161     }
162     
163     // Trim output buffer to the actual amount of data written
164     out->size = dst - out->data;
165     
166     // Copy metadata from the input packet to the output packet
167     out->start = in->start;
168     out->stop = in->stop;
169     
170 fail:
171     free( startStyle );
172     free( endStyle );
173     
174     return out;
175 }
176
177 #undef READ_U8
178 #undef READ_U16
179 #undef READ_U32
180 #undef READ_ARRAY
181 #undef SKIP_ARRAY
182
183 #undef WRITE_CHAR
184 #undef WRITE_START_TAG
185 #undef WRITE_END_TAG
186
187 static int dectx3gInit( hb_work_object_t * w, hb_job_t * job )
188 {
189     return 0;
190 }
191
192 static int dectx3gWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
193                         hb_buffer_t ** buf_out )
194 {
195     hb_buffer_t * in = *buf_in;
196     hb_buffer_t * out = NULL;
197     
198     // Warn if the subtitle's duration has not been passed through by the demuxer,
199     // which will prevent the subtitle from displaying at all
200     if ( in->stop == 0 ) {
201         hb_log( "dectx3gsub: subtitle packet lacks duration" );
202     }
203     
204     if ( in->size > 0 ) {
205         out = tx3g_decode_to_utf8(in);
206     } else {
207         out = hb_buffer_init( 0 );
208     }
209     
210     if ( out != NULL ) {
211         // We shouldn't be storing the extra NULL character,
212         // but the MP4 muxer expects this, unfortunately.
213         if ( out->size > 0 && out->data[out->size - 1] != '\0' ) {
214             // NOTE: out->size remains unchanged
215             hb_buffer_realloc( out, out->size + 1 );
216             out->data[out->size] = '\0';
217         }
218         
219         // If the input packet was non-empty, do not pass through
220         // an empty output packet (even if the subtitle was empty),
221         // as this would be interpreted as an end-of-stream
222         if ( in->size > 0 && out->size == 0 ) {
223             hb_buffer_close(&out);
224         }
225     }
226     
227     // Dispose the input packet, as it is no longer needed
228     hb_buffer_close(&in);
229     
230     *buf_in = NULL;
231     *buf_out = out;
232     return HB_WORK_OK;
233 }
234
235 static void dectx3gClose( hb_work_object_t * w )
236 {
237     // nothing
238 }
239
240 hb_work_object_t hb_dectx3gsub =
241 {
242     WORK_DECTX3GSUB,
243     "TX3G Subtitle Decoder",
244     dectx3gInit,
245     dectx3gWork,
246     dectx3gClose
247 };