source: titan/minidlna-1.0.22/tagutils/tagutils-asf.c @ 13567

Last change on this file since 13567 was 13567, checked in by obi, 11 years ago

[titan] add minidlna-1.0.22 first step

File size: 13.3 KB
Line 
1//=========================================================================
2// FILENAME     : tagutils-asf.c
3// DESCRIPTION  : ASF (wma/wmv) metadata reader
4//=========================================================================
5// Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved.
6//=========================================================================
7
8/*
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23static int
24_asf_read_file_properties(FILE *fp, asf_file_properties_t *p, __u32 size)
25{
26        int len;
27
28        len = sizeof(*p) - offsetof(asf_file_properties_t, FileID);
29        if(size < len)
30                return -1;
31
32        memset(p, 0, sizeof(*p));
33        p->ID = ASF_FileProperties;
34        p->Size = size;
35
36        if(len != fread(&p->FileID, 1, len, fp))
37                return -1;
38
39        return 0;
40}
41
42static void
43_pick_dlna_profile(struct song_metadata *psong, uint16_t format)
44{
45        /* DLNA Profile Name */
46        switch( le16_to_cpu(format) )
47        {
48                case WMA:
49                        if( psong->max_bitrate < 193000 )
50                                asprintf(&(psong->dlna_pn), "WMABASE");
51                        else if( psong->max_bitrate < 385000 )
52                                asprintf(&(psong->dlna_pn), "WMAFULL");
53                        break;
54                case WMAPRO:
55                        asprintf(&(psong->dlna_pn), "WMAPRO");
56                        break;
57                case WMALSL:
58                        asprintf(&(psong->dlna_pn), "WMALSL%s",
59                                psong->channels > 2 ? "_MULT5" : "");
60                default:
61                        break;
62        }
63}
64
65static int
66_asf_read_audio_stream(FILE *fp, struct song_metadata *psong, int size)
67{
68        asf_audio_stream_t s;
69        int len;
70
71        len = sizeof(s) - sizeof(s.Hdr);
72        if(len > size)
73                len = size;
74
75        if(len != fread(&s.wfx, 1, len, fp))
76                return -1;
77
78        psong->channels = le16_to_cpu(s.wfx.nChannels);
79        psong->bitrate = le32_to_cpu(s.wfx.nAvgBytesPerSec) * 8;
80        psong->samplerate = le32_to_cpu(s.wfx.nSamplesPerSec);
81        if (!psong->max_bitrate)
82                psong->max_bitrate = psong->bitrate;
83        _pick_dlna_profile(psong, s.wfx.wFormatTag);
84
85        return 0;
86}
87
88static int
89_asf_read_media_stream(FILE *fp, struct song_metadata *psong, __u32 size)
90{
91        asf_media_stream_t s;
92        avi_audio_format_t wfx;
93        int len;
94
95        len = sizeof(s) - sizeof(s.Hdr);
96        if(len > size)
97                len = size;
98
99        if(len != fread(&s.MajorType, 1, len, fp))
100                return -1;
101
102        if(IsEqualGUID(&s.MajorType, &ASF_MediaTypeAudio) &&
103           IsEqualGUID(&s.FormatType, &ASF_FormatTypeWave) && s.FormatSize >= sizeof(wfx))
104        {
105
106                if(sizeof(wfx) != fread(&wfx, 1, sizeof(wfx), fp))
107                        return -1;
108
109                psong->channels = le16_to_cpu(wfx.nChannels);
110                psong->bitrate = le32_to_cpu(wfx.nAvgBytesPerSec) * 8;
111                psong->samplerate = le32_to_cpu(wfx.nSamplesPerSec);
112                if (!psong->max_bitrate)
113                        psong->max_bitrate = psong->bitrate;
114                _pick_dlna_profile(psong, wfx.wFormatTag);
115        }
116
117        return 0;
118}
119
120static int
121_asf_read_stream_object(FILE *fp, struct song_metadata *psong, __u32 size)
122{
123        asf_stream_object_t s;
124        int len;
125
126        len = sizeof(s) - sizeof(asf_object_t);
127        if(size < len)
128                return -1;
129
130        if(len != fread(&s.StreamType, 1, len, fp))
131                return -1;
132
133        if(IsEqualGUID(&s.StreamType, &ASF_AudioStream))
134                _asf_read_audio_stream(fp, psong, s.TypeSpecificSize);
135        else if(IsEqualGUID(&s.StreamType, &ASF_StreamBufferStream))
136                _asf_read_media_stream(fp, psong, s.TypeSpecificSize);
137        else if(!IsEqualGUID(&s.StreamType, &ASF_VideoStream))
138        {
139                DPRINTF(E_ERROR, L_SCANNER, "Unknown asf stream type.\n");
140        }
141
142        return 0;
143}
144
145static int
146_asf_read_extended_stream_object(FILE *fp, struct song_metadata *psong, __u32 size)
147{
148        int i, len;
149        long off;
150        asf_object_t tmp;
151        asf_extended_stream_object_t xs;
152        asf_stream_name_t nm;
153        asf_payload_extension_t pe;
154
155        if(size < sizeof(asf_extended_stream_object_t))
156                return -1;
157
158        len = sizeof(xs) - offsetof(asf_extended_stream_object_t, StartTime);
159        if(len != fread(&xs.StartTime, 1, len, fp))
160                return -1;
161        off = sizeof(xs);
162
163        for(i = 0; i < xs.StreamNameCount; i++)
164        {
165                if(off + sizeof(nm) > size)
166                        return -1;
167                if(sizeof(nm) != fread(&nm, 1, sizeof(nm), fp))
168                        return -1;
169                off += sizeof(nm);
170                if(off + nm.Length > sizeof(asf_extended_stream_object_t))
171                        return -1;
172                if(nm.Length > 0)
173                        fseek(fp, nm.Length, SEEK_CUR);
174                off += nm.Length;
175        }
176
177        for(i = 0; i < xs.PayloadExtensionSystemCount; i++)
178        {
179                if(off + sizeof(pe) > size)
180                        return -1;
181                if(sizeof(pe) != fread(&pe, 1, sizeof(pe), fp))
182                        return -1;
183                off += sizeof(pe);
184                if(pe.InfoLength > 0)
185                        fseek(fp, pe.InfoLength, SEEK_CUR);
186                off += pe.InfoLength;
187        }
188
189        if(off < size)
190        {
191                if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp))
192                        return -1;
193                if(IsEqualGUID(&tmp.ID, &ASF_StreamHeader))
194                        _asf_read_stream_object(fp, psong, tmp.Size);
195        }
196
197        return 0;
198}
199
200static int
201_asf_read_header_extension(FILE *fp, struct song_metadata *psong, __u32 size)
202{
203        off_t pos;
204        long off;
205        asf_header_extension_t ext;
206        asf_object_t tmp;
207
208        if(size < sizeof(asf_header_extension_t))
209                return -1;
210
211        fread(&ext.Reserved1, 1, sizeof(ext.Reserved1), fp);
212        ext.Reserved2 = fget_le16(fp);
213        ext.DataSize = fget_le32(fp);
214
215        pos = ftell(fp);
216        off = 0;
217        while(off < ext.DataSize)
218        {
219                if(sizeof(asf_header_extension_t) + off > size)
220                        break;
221                if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp))
222                        break;
223                if(off + tmp.Size > ext.DataSize)
224                        break;
225                if(IsEqualGUID(&tmp.ID, &ASF_ExtendedStreamPropertiesObject))
226                        _asf_read_extended_stream_object(fp, psong, tmp.Size);
227
228                off += tmp.Size;
229                fseek(fp, pos + off, SEEK_SET);
230        }
231
232        return 0;
233}
234
235static int
236_asf_load_string(FILE *fp, int type, int size, char *buf, int len)
237{
238        unsigned char data[2048];
239        __u16 wc;
240        int i, j;
241        __s32 *wd32;
242        __s64 *wd64;
243        __s16 *wd16;
244
245        i = 0;
246        if(size && (size <= sizeof(data)) && (size == fread(data, 1, size, fp)))
247        {
248
249                switch(type)
250                {
251                case ASF_VT_UNICODE:
252                        for(j = 0; j < size; j += 2)
253                        {
254                                wc = *(__s16*)&data[j];
255                                i += utf16le_to_utf8(&buf[i], len - i, wc);
256                        }
257                        break;
258                case ASF_VT_BYTEARRAY:
259                        for(i = 0; i < size; i++)
260                        {
261                                if(i + 1 >= len)
262                                        break;
263                                buf[i] = data[i];
264                        }
265                        break;
266                case ASF_VT_BOOL:
267                case ASF_VT_DWORD:
268                        if(size >= 4)
269                        {
270                                wd32 = (__s32 *) &data[0];
271                                i = snprintf(buf, len, "%d", le32_to_cpu(*wd32));
272                        }
273                        break;
274                case ASF_VT_QWORD:
275                        if(size >= 8)
276                        {
277                                wd64 = (__s64 *) &data[0];
278#if __WORDSIZE == 64
279                                i = snprintf(buf, len, "%ld", le64_to_cpu(*wd64));
280#else
281                                i = snprintf(buf, len, "%lld", le64_to_cpu(*wd64));
282#endif
283                        }
284                        break;
285                case ASF_VT_WORD:
286                        if(size >= 2)
287                        {
288                                wd16 = (__s16 *) &data[0];
289                                i = snprintf(buf, len, "%d", le16_to_cpu(*wd16));
290                        }
291                        break;
292                }
293
294                size = 0;
295        }
296        else fseek(fp, size, SEEK_CUR);
297
298        buf[i] = 0;
299        return i;
300}
301
302static void *
303_asf_load_picture(FILE *fp, int size, void *bm, int *bm_size)
304{
305        int i;
306        char buf[256];
307#if 0
308        //
309        // Picture type       $xx
310        // Data length    $xx $xx $xx $xx
311        // MIME type          <text string> $00
312        // Description        <text string> $00
313        // Picture data       <binary data>
314
315        char pic_type;
316        long pic_size;
317
318        pic_type = fget_byte(fp); size -= 1;
319        pic_size = fget_le32(fp); size -= 4;
320#else
321        fseek(fp, 5, SEEK_CUR);
322        size -= 5;
323#endif
324        for(i = 0; i < sizeof(buf) - 1; i++)
325        {
326                buf[i] = fget_le16(fp); size -= 2;
327                if(!buf[i])
328                        break;
329        }
330        buf[i] = '\0';
331        if(i == sizeof(buf) - 1)
332        {
333                while(fget_le16(fp))
334                        size -= 2;
335        }
336
337        if(!strcasecmp(buf, "image/jpeg") ||
338           !strcasecmp(buf, "image/jpg") ||
339           !strcasecmp(buf, "image/peg"))
340        {
341
342                while(0 != fget_le16(fp))
343                        size -= 2;
344
345                if(size > 0)
346                {
347                        if(!(bm = malloc(size)))
348                        {
349                                DPRINTF(E_ERROR, L_SCANNER, "Couldn't allocate %d bytes\n", size);
350                        }
351                        else
352                        {
353                                *bm_size = size;
354                                if(size <= *bm_size)
355                                {
356                                        fread(bm, 1, size, fp);
357                                }
358                                else
359                                {
360                                        DPRINTF(E_ERROR, L_SCANNER, "Overrun %d bytes required\n", size);
361                                        free(bm);
362                                        bm = NULL;
363                                }
364                        }
365                }
366                else
367                {
368                        DPRINTF(E_ERROR, L_SCANNER, "No binary data\n");
369                        size = 0;
370                        bm = NULL;
371                }
372        }
373        else
374        {
375                DPRINTF(E_ERROR, L_SCANNER, "Invalid mime type %s\n", buf);
376        }
377
378        *bm_size = size;
379        return bm;
380}
381
382static int
383_get_asffileinfo(char *file, struct song_metadata *psong)
384{
385        FILE *fp;
386        asf_object_t hdr;
387        asf_object_t tmp;
388        unsigned long NumObjects;
389        unsigned short TitleLength;
390        unsigned short AuthorLength;
391        unsigned short CopyrightLength;
392        unsigned short DescriptionLength;
393        unsigned short RatingLength;
394        unsigned short NumEntries;
395        unsigned short NameLength;
396        unsigned short ValueType;
397        unsigned short ValueLength;
398        off_t pos;
399        char buf[2048];
400        asf_file_properties_t FileProperties;
401
402        psong->vbr_scale = -1;
403
404        if(!(fp = fopen(file, "rb")))
405        {
406                DPRINTF(E_ERROR, L_SCANNER, "Could not open %s for reading\n", file);
407                return -1;
408        }
409
410        if(sizeof(hdr) != fread(&hdr, 1, sizeof(hdr), fp))
411        {
412                DPRINTF(E_ERROR, L_SCANNER, "Error reading %s\n", file);
413                fclose(fp);
414                return -1;
415        }
416        hdr.Size = le64_to_cpu(hdr.Size);
417
418        if(!IsEqualGUID(&hdr.ID, &ASF_HeaderObject))
419        {
420                DPRINTF(E_ERROR, L_SCANNER, "Not a valid header\n");
421                fclose(fp);
422                return -1;
423        }
424        NumObjects = fget_le32(fp);
425        fseek(fp, 2, SEEK_CUR); // Reserved le16
426
427        pos = ftell(fp);
428        while(NumObjects > 0)
429        {
430                if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp))
431                        break;
432                tmp.Size = le64_to_cpu(tmp.Size);
433
434                if(pos + tmp.Size > hdr.Size)
435                {
436                        DPRINTF(E_ERROR, L_SCANNER, "Size overrun reading header object %I64x\n", tmp.Size);
437                        break;
438                }
439
440                if(IsEqualGUID(&tmp.ID, &ASF_FileProperties))
441                {
442                        _asf_read_file_properties(fp, &FileProperties, tmp.Size);
443                        psong->song_length = le64_to_cpu(FileProperties.PlayDuration) / 10000;
444                        psong->bitrate = le64_to_cpu(FileProperties.MaxBitrate);
445                        psong->max_bitrate = psong->bitrate;
446                }
447                else if(IsEqualGUID(&tmp.ID, &ASF_ContentDescription))
448                {
449                        TitleLength = fget_le16(fp);
450                        AuthorLength = fget_le16(fp);
451                        CopyrightLength = fget_le16(fp);
452                        DescriptionLength = fget_le16(fp);
453                        RatingLength = fget_le16(fp);
454
455                        if(_asf_load_string(fp, ASF_VT_UNICODE, TitleLength, buf, sizeof(buf)))
456                        {
457                                if(buf[0])
458                                        psong->title = strdup(buf);
459                        }
460                        if(_asf_load_string(fp, ASF_VT_UNICODE, AuthorLength, buf, sizeof(buf)))
461                        {
462                                if(buf[0])
463                                        psong->contributor[ROLE_TRACKARTIST] = strdup(buf);
464                        }
465                        if(CopyrightLength)
466                                fseek(fp, CopyrightLength, SEEK_CUR);
467                        if(DescriptionLength)
468                                fseek(fp, DescriptionLength, SEEK_CUR);
469                        if(RatingLength)
470                                fseek(fp, RatingLength, SEEK_CUR);
471                }
472                else if(IsEqualGUID(&tmp.ID, &ASF_ExtendedContentDescription))
473                {
474                        NumEntries = fget_le16(fp);
475                        while(NumEntries > 0)
476                        {
477                                NameLength = fget_le16(fp);
478                                _asf_load_string(fp, ASF_VT_UNICODE, NameLength, buf, sizeof(buf));
479                                ValueType = fget_le16(fp);
480                                ValueLength = fget_le16(fp);
481
482                                if(!strcasecmp(buf, "AlbumTitle") || !strcasecmp(buf, "WM/AlbumTitle"))
483                                {
484                                        if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
485                                                if(buf[0])
486                                                        psong->album = strdup(buf);
487                                }
488                                else if(!strcasecmp(buf, "AlbumArtist") || !strcasecmp(buf, "WM/AlbumArtist"))
489                                {
490                                        if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
491                                        {
492                                                if(buf[0])
493                                                        psong->contributor[ROLE_ALBUMARTIST] = strdup(buf);
494                                        }
495                                }
496                                else if(!strcasecmp(buf, "Description") || !strcasecmp(buf, "WM/Track"))
497                                {
498                                        if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
499                                                if(buf[0])
500                                                        psong->track = atoi(buf);
501                                }
502                                else if(!strcasecmp(buf, "Genre") || !strcasecmp(buf, "WM/Genre"))
503                                {
504                                        if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
505                                                if(buf[0])
506                                                        psong->genre = strdup(buf);
507                                }
508                                else if(!strcasecmp(buf, "Year") || !strcasecmp(buf, "WM/Year"))
509                                {
510                                        if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
511                                                if(buf[0])
512                                                        psong->year = atoi(buf);
513                                }
514                                else if(!strcasecmp(buf, "WM/Director"))
515                                {
516                                        if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
517                                                if(buf[0])
518                                                        psong->contributor[ROLE_CONDUCTOR] = strdup(buf);
519                                }
520                                else if(!strcasecmp(buf, "WM/Composer"))
521                                {
522                                        if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
523                                                if(buf[0])
524                                                        psong->contributor[ROLE_COMPOSER] = strdup(buf);
525                                }
526                                else if(!strcasecmp(buf, "WM/Picture") && (ValueType == ASF_VT_BYTEARRAY))
527                                {
528                                        psong->image = _asf_load_picture(fp, ValueLength, psong->image, &psong->image_size);
529                                }
530                                else if(!strcasecmp(buf, "TrackNumber") || !strcasecmp(buf, "WM/TrackNumber"))
531                                {
532                                        if(_asf_load_string(fp, ValueType, ValueLength, buf, sizeof(buf)))
533                                                if(buf[0])
534                                                        psong->track = atoi(buf);
535                                }
536                                else if(!strcasecmp(buf, "isVBR"))
537                                {
538                                        fseek(fp, ValueLength, SEEK_CUR);
539                                        psong->vbr_scale = 0;
540                                }
541                                else if(ValueLength)
542                                {
543                                        fseek(fp, ValueLength, SEEK_CUR);
544                                }
545                                NumEntries--;
546                        }
547                }
548                else if(IsEqualGUID(&tmp.ID, &ASF_StreamHeader))
549                {
550                        _asf_read_stream_object(fp, psong, tmp.Size);
551                }
552                else if(IsEqualGUID(&tmp.ID, &ASF_HeaderExtension))
553                {
554                        _asf_read_header_extension(fp, psong, tmp.Size);
555                }
556                pos += tmp.Size;
557                fseek(fp, pos, SEEK_SET);
558                NumObjects--;
559        }
560
561#if 0
562        if(sizeof(hdr) == fread(&hdr, 1, sizeof(hdr), fp) && IsEqualGUID(&hdr.ID, &ASF_DataObject))
563        {
564                if(psong->song_length)
565                {
566                        psong->bitrate = (hdr.Size * 8000) / psong->song_length;
567                }
568        }
569#endif
570
571        fclose(fp);
572        return 0;
573}
Note: See TracBrowser for help on using the repository browser.