Browse code

lavf/brstm: support little-endian files (BCSTM; 3DS)

Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

Rodger Combs authored on 2015/06/20 19:01:15
Showing 2 changed files
... ...
@@ -3,7 +3,7 @@ releases are sorted from youngest to oldest.
3 3
 
4 4
 version <next>:
5 5
 - colorkey video filter
6
-- BFSTM demuxer
6
+- BFSTM/BCSTM demuxer
7 7
 - little-endian ADPCM_THP decoder
8 8
 
9 9
 
... ...
@@ -33,6 +33,7 @@ typedef struct BRSTMDemuxContext {
33 33
     uint8_t     *table;
34 34
     uint8_t     *adpc;
35 35
     int         bfstm;
36
+    int         little_endian;
36 37
 } BRSTMDemuxContext;
37 38
 
38 39
 static int probe(AVProbeData *p)
... ...
@@ -46,7 +47,8 @@ static int probe(AVProbeData *p)
46 46
 
47 47
 static int probe_bfstm(AVProbeData *p)
48 48
 {
49
-    if (AV_RL32(p->buf) == MKTAG('F','S','T','M') &&
49
+    if ((AV_RL32(p->buf) == MKTAG('F','S','T','M') ||
50
+         AV_RL32(p->buf) == MKTAG('C','S','T','M')) &&
50 51
         (AV_RL16(p->buf + 4) == 0xFFFE ||
51 52
          AV_RL16(p->buf + 4) == 0xFEFF))
52 53
         return AVPROBE_SCORE_MAX / 3 * 2;
... ...
@@ -63,6 +65,24 @@ static int read_close(AVFormatContext *s)
63 63
     return 0;
64 64
 }
65 65
 
66
+static av_always_inline unsigned int read16(AVFormatContext *s)
67
+{
68
+    BRSTMDemuxContext *b = s->priv_data;
69
+    if (b->little_endian)
70
+        return avio_rl16(s->pb);
71
+    else
72
+        return avio_rb16(s->pb);
73
+}
74
+
75
+static av_always_inline unsigned int read32(AVFormatContext *s)
76
+{
77
+    BRSTMDemuxContext *b = s->priv_data;
78
+    if (b->little_endian)
79
+        return avio_rl32(s->pb);
80
+    else
81
+        return avio_rb32(s->pb);
82
+}
83
+
66 84
 static int read_header(AVFormatContext *s)
67 85
 {
68 86
     BRSTMDemuxContext *b = s->priv_data;
... ...
@@ -86,16 +106,15 @@ static int read_header(AVFormatContext *s)
86 86
         av_log(s, AV_LOG_ERROR, "invalid byte order: %X\n", bom);
87 87
         return AVERROR_INVALIDDATA;
88 88
     }
89
-    if (bom == 0xFFFE) {
90
-        avpriv_request_sample(s, "little endian byte order");
91
-        return AVERROR_PATCHWELCOME;
92
-    }
89
+
90
+    if (bom == 0xFFFE)
91
+        b->little_endian = 1;
93 92
 
94 93
     if (!b->bfstm) {
95 94
         major = avio_r8(s->pb);
96 95
         minor = avio_r8(s->pb);
97 96
         avio_skip(s->pb, 4); // size of file
98
-        size = avio_rb16(s->pb);
97
+        size = read16(s);
99 98
         if (size < 14)
100 99
             return AVERROR_INVALIDDATA;
101 100
 
... ...
@@ -107,30 +126,31 @@ static int read_header(AVFormatContext *s)
107 107
         uint32_t info_offset = 0, info_size;
108 108
         uint16_t section_count, header_size, i;
109 109
 
110
-        header_size = avio_rb16(s->pb); // 6
110
+        header_size = read16(s); // 6
111 111
 
112 112
         avio_skip(s->pb, 4); // Unknown constant 0x00030000
113 113
         avio_skip(s->pb, 4); // size of file
114
-        section_count = avio_rb16(s->pb);
114
+        section_count = read16(s);
115 115
         avio_skip(s->pb, 2); // padding
116 116
         for (i = 0; avio_tell(s->pb) < header_size
117 117
                     && !(data_offset && info_offset)
118 118
                     && i < section_count; i++) {
119
-            uint32_t flag = avio_rb32(s->pb);
119
+            uint16_t flag = read16(s);
120
+            avio_skip(s->pb, 2);
120 121
             switch (flag) {
121
-            case 0x40000000:
122
-                info_offset = avio_rb32(s->pb);
123
-                info_size   = avio_rb32(s->pb);
122
+            case 0x4000:
123
+                info_offset = read32(s);
124
+                info_size   = read32(s);
124 125
                 break;
125
-            case 0x40010000:
126
+            case 0x4001:
126 127
                 avio_skip(s->pb, 4); // seek offset
127 128
                 avio_skip(s->pb, 4); // seek size
128 129
                 break;
129
-            case 0x40020000:
130
-                data_offset = avio_rb32(s->pb);
131
-                avio_skip(s->pb, 4); //data_size = avio_rb32(s->pb);
130
+            case 0x4002:
131
+                data_offset = read32(s);
132
+                avio_skip(s->pb, 4); //data_size = read32(s);
132 133
                 break;
133
-            case 0x40030000:
134
+            case 0x4003:
134 135
                 avio_skip(s->pb, 4); // REGN offset
135 136
                 avio_skip(s->pb, 4); // REGN size
136 137
                 break;
... ...
@@ -148,15 +168,15 @@ static int read_header(AVFormatContext *s)
148 148
             return AVERROR_INVALIDDATA;
149 149
     }
150 150
 
151
-    size = avio_rb32(s->pb);
151
+    size = read32(s);
152 152
     if (size < 192)
153 153
         return AVERROR_INVALIDDATA;
154 154
     avio_skip(s->pb, 4); // unknown
155
-    h1offset = avio_rb32(s->pb);
155
+    h1offset = read32(s);
156 156
     if (h1offset > size)
157 157
         return AVERROR_INVALIDDATA;
158 158
     avio_skip(s->pb, 12);
159
-    toffset = avio_rb32(s->pb) + 16LL;
159
+    toffset = read32(s) + 16LL;
160 160
     if (toffset > size)
161 161
         return AVERROR_INVALIDDATA;
162 162
 
... ...
@@ -166,7 +186,9 @@ static int read_header(AVFormatContext *s)
166 166
     switch (codec) {
167 167
     case 0: codec = AV_CODEC_ID_PCM_S8_PLANAR;    break;
168 168
     case 1: codec = AV_CODEC_ID_PCM_S16BE_PLANAR; break;
169
-    case 2: codec = AV_CODEC_ID_ADPCM_THP;        break;
169
+    case 2: codec = b->little_endian ?
170
+                    AV_CODEC_ID_ADPCM_THP_LE :
171
+                    AV_CODEC_ID_ADPCM_THP;        break;
170 172
     default:
171 173
         avpriv_request_sample(s, "codec %d", codec);
172 174
         return AVERROR_PATCHWELCOME;
... ...
@@ -179,10 +201,8 @@ static int read_header(AVFormatContext *s)
179 179
         return AVERROR_INVALIDDATA;
180 180
 
181 181
     avio_skip(s->pb, 1); // padding
182
-    if (b->bfstm)
183
-        avio_skip(s->pb, 2); // padding
184 182
 
185
-    st->codec->sample_rate = avio_rb16(s->pb);
183
+    st->codec->sample_rate = b->bfstm ? read32(s) : read16(s);
186 184
     if (!st->codec->sample_rate)
187 185
         return AVERROR_INVALIDDATA;
188 186
 
... ...
@@ -190,25 +210,25 @@ static int read_header(AVFormatContext *s)
190 190
         avio_skip(s->pb, 2); // padding
191 191
     avio_skip(s->pb, 4); // loop start sample
192 192
     st->start_time = 0;
193
-    st->duration = avio_rb32(s->pb);
193
+    st->duration = read32(s);
194 194
     avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
195 195
 
196 196
     if (!b->bfstm)
197
-        start = avio_rb32(s->pb);
197
+        start = read32(s);
198 198
     b->current_block = 0;
199
-    b->block_count = avio_rb32(s->pb);
199
+    b->block_count = read32(s);
200 200
     if (b->block_count > UINT16_MAX) {
201 201
         av_log(s, AV_LOG_WARNING, "too many blocks: %u\n", b->block_count);
202 202
         return AVERROR_INVALIDDATA;
203 203
     }
204 204
 
205
-    b->block_size = avio_rb32(s->pb);
205
+    b->block_size = read32(s);
206 206
     if (b->block_size > UINT16_MAX / st->codec->channels)
207 207
         return AVERROR_INVALIDDATA;
208 208
     b->block_size *= st->codec->channels;
209 209
 
210
-    b->samples_per_block = avio_rb32(s->pb);
211
-    b->last_block_used_bytes = avio_rb32(s->pb);
210
+    b->samples_per_block = read32(s);
211
+    b->last_block_used_bytes = read32(s);
212 212
     if (b->last_block_used_bytes > UINT16_MAX / st->codec->channels)
213 213
         return AVERROR_INVALIDDATA;
214 214
     b->last_block_used_bytes *= st->codec->channels;
... ...
@@ -216,14 +236,14 @@ static int read_header(AVFormatContext *s)
216 216
     avio_skip(s->pb, 4); // last block samples
217 217
     avio_skip(s->pb, 4); // last block size
218 218
 
219
-    if (codec == AV_CODEC_ID_ADPCM_THP) {
219
+    if (codec == AV_CODEC_ID_ADPCM_THP || codec == AV_CODEC_ID_ADPCM_THP_LE) {
220 220
         int ch;
221 221
 
222 222
         avio_skip(s->pb, pos + toffset - avio_tell(s->pb));
223 223
         if (!b->bfstm)
224
-            toffset = avio_rb32(s->pb) + 16LL;
224
+            toffset = read32(s) + 16LL;
225 225
         else
226
-            toffset = toffset + avio_rb32(s->pb) + st->codec->channels * 8 - 8;
226
+            toffset = toffset + read32(s) + st->codec->channels * 8 - 8;
227 227
         if (toffset > size)
228 228
             return AVERROR_INVALIDDATA;
229 229
 
... ...
@@ -261,7 +281,7 @@ static int read_header(AVFormatContext *s)
261 261
 
262 262
     while (!avio_feof(s->pb)) {
263 263
         chunk = avio_rl32(s->pb);
264
-        size  = avio_rb32(s->pb);
264
+        size  = read32(s);
265 265
         if (size < 8) {
266 266
             ret = AVERROR_INVALIDDATA;
267 267
             goto fail;
... ...
@@ -269,7 +289,8 @@ static int read_header(AVFormatContext *s)
269 269
         size -= 8;
270 270
         switch (chunk) {
271 271
         case MKTAG('A','D','P','C'):
272
-            if (codec != AV_CODEC_ID_ADPCM_THP)
272
+            if (codec != AV_CODEC_ID_ADPCM_THP &&
273
+                codec != AV_CODEC_ID_ADPCM_THP_LE)
273 274
                 goto skip;
274 275
 
275 276
             asize = b->block_count * st->codec->channels * 4;
... ...
@@ -292,7 +313,9 @@ static int read_header(AVFormatContext *s)
292 292
             break;
293 293
         case MKTAG('D','A','T','A'):
294 294
             if ((start < avio_tell(s->pb)) ||
295
-                (!b->adpc && codec == AV_CODEC_ID_ADPCM_THP && !b->bfstm)) {
295
+                (!b->adpc && (codec == AV_CODEC_ID_ADPCM_THP ||
296
+                              codec == AV_CODEC_ID_ADPCM_THP_LE)
297
+                          && !b->bfstm)) {
296 298
                 ret = AVERROR_INVALIDDATA;
297 299
                 goto fail;
298 300
             }
... ...
@@ -335,7 +358,8 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt)
335 335
         return AVERROR_EOF;
336 336
     }
337 337
 
338
-    if (codec->codec_id == AV_CODEC_ID_ADPCM_THP && !codec->extradata) {
338
+    if ((codec->codec_id == AV_CODEC_ID_ADPCM_THP ||
339
+         codec->codec_id == AV_CODEC_ID_ADPCM_THP_LE) && !codec->extradata) {
339 340
         uint8_t *dst;
340 341
 
341 342
         if (av_new_packet(pkt, 8 + (32 + 4) * codec->channels + size) < 0)
... ...
@@ -382,5 +406,5 @@ AVInputFormat ff_bfstm_demuxer = {
382 382
     .read_header    = read_header,
383 383
     .read_packet    = read_packet,
384 384
     .read_close     = read_close,
385
-    .extensions     = "bfstm",
385
+    .extensions     = "bfstm,bcstm",
386 386
 };