OSDN Git Service

fix compiler warnings in several libhb files
[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     uint8_t *dst = out->data;
132     int charIndex = 0;
133     for ( pos = text, end = text + textLength; pos < end; pos++ ) {
134         if (IS_10xxxxxx(*pos)) {
135             // Is a non-first byte of a multi-byte UTF-8 character
136             WRITE_CHAR(*pos);
137             continue;   // ...without incrementing 'charIndex'
138         }
139         
140         uint8_t plusStyles = startStyle[charIndex];
141         uint8_t minusStyles = endStyle[charIndex];
142         
143         if (minusStyles & UNDERLINE)
144             WRITE_END_TAG('u');
145         if (minusStyles & ITALIC)
146             WRITE_END_TAG('i');
147         if (minusStyles & BOLD)
148             WRITE_END_TAG('b');
149         
150         if (plusStyles & BOLD)
151             WRITE_START_TAG('b');
152         if (plusStyles & ITALIC)
153             WRITE_START_TAG('i');
154         if (plusStyles & UNDERLINE)
155             WRITE_START_TAG('u');
156         
157         WRITE_CHAR(*pos);
158         charIndex++;
159     }
160     
161     // Trim output buffer to the actual amount of data written
162     out->size = dst - out->data;
163     
164     // Copy metadata from the input packet to the output packet
165     out->start = in->start;
166     out->stop = in->stop;
167     
168     free( startStyle );
169     free( endStyle );
170     
171     return out;
172 }
173
174 #undef READ_U8
175 #undef READ_U16
176 #undef READ_U32
177 #undef READ_ARRAY
178 #undef SKIP_ARRAY
179
180 #undef WRITE_CHAR
181 #undef WRITE_START_TAG
182 #undef WRITE_END_TAG
183
184 static int dectx3gInit( hb_work_object_t * w, hb_job_t * job )
185 {
186     return 0;
187 }
188
189 static int dectx3gWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
190                         hb_buffer_t ** buf_out )
191 {
192     hb_buffer_t * in = *buf_in;
193     hb_buffer_t * out = NULL;
194     
195     // Warn if the subtitle's duration has not been passed through by the demuxer,
196     // which will prevent the subtitle from displaying at all
197     if ( in->stop == 0 ) {
198         hb_log( "dectx3gsub: subtitle packet lacks duration" );
199     }
200     
201     if ( in->size > 0 ) {
202         out = tx3g_decode_to_utf8(in);
203     } else {
204         out = hb_buffer_init( 0 );
205     }
206     
207     // We shouldn't be storing the extra NULL character,
208     // but the MP4 muxer expects this, unfortunately.
209     if ( out->size > 0 && out->data[out->size - 1] != '\0' ) {
210         // NOTE: out->size remains unchanged
211         hb_buffer_realloc( out, out->size + 1 );
212         out->data[out->size] = '\0';
213     }
214     
215     // If the input packet was non-empty, do not pass through
216     // an empty output packet (even if the subtitle was empty),
217     // as this would be interpreted as an end-of-stream
218     if ( in->size > 0 && out->size == 0 ) {
219         hb_buffer_close(&out);
220     }
221     
222     // Dispose the input packet, as it is no longer needed
223     hb_buffer_close(&in);
224     
225     *buf_in = NULL;
226     *buf_out = out;
227     return HB_WORK_OK;
228 }
229
230 static void dectx3gClose( hb_work_object_t * w )
231 {
232     // nothing
233 }
234
235 hb_work_object_t hb_dectx3gsub =
236 {
237     WORK_DECTX3GSUB,
238     "TX3G Subtitle Decoder",
239     dectx3gInit,
240     dectx3gWork,
241     dectx3gClose
242 };