demux_lavf: don't autoselect attached pictures as video
[mplayer.git] / codec-cfg.c
blob4f2027eeb6e59dfe1a5cb24b06b37bf208ab1ac6
1 /*
2 * codecs.conf parser
4 * Copyright (C) 2001 Szabolcs Berecz <szabi@inf.elte.hu>
6 * This file is part of MPlayer.
8 * MPlayer is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * MPlayer is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <assert.h>
30 #include <string.h>
31 #include <stdint.h>
33 #include "config.h"
34 #include "mp_msg.h"
35 #include "libmpcodecs/img_format.h"
36 #include "codec-cfg.h"
37 #include "bstr.h"
38 #include "stream/stream.h"
39 #include "path.h"
41 static const char embedded_file[] =
42 #include "codecs.conf.h"
44 static const struct bstr builtin_codecs_conf = {
45 .start = (char *)embedded_file, .len = sizeof(embedded_file) - 1
48 #define mmioFOURCC( ch0, ch1, ch2, ch3 ) \
49 ( (uint32_t)(uint8_t)(ch0) | ( (uint32_t)(uint8_t)(ch1) << 8 ) | \
50 ( (uint32_t)(uint8_t)(ch2) << 16 ) | ( (uint32_t)(uint8_t)(ch3) << 24 ) )
52 #define PRINT_LINENUM mp_msg(MSGT_CODECCFG,MSGL_ERR," at line %d\n", line_num)
54 #define MAX_NR_TOKEN 16
56 #define RET_EOF -1
57 #define RET_EOL -2
59 #define TYPE_VIDEO 0
60 #define TYPE_AUDIO 1
62 static int codecs_conf_release;
63 char * codecs_file = NULL;
65 static int add_to_fourcc(char *s, char *alias, unsigned int *fourcc,
66 unsigned int *map)
68 int i, j, freeslots;
69 unsigned int tmp;
71 /* find first unused slot */
72 for (i = 0; i < CODECS_MAX_FOURCC && fourcc[i] != 0xffffffff; i++)
73 /* NOTHING */;
74 freeslots = CODECS_MAX_FOURCC - i;
75 if (!freeslots)
76 goto err_out_too_many;
78 do {
79 if (strlen(s) < 4)
80 goto err_out_parse_error;
81 tmp = mmioFOURCC(s[0], s[1], s[2], s[3]);
82 for (j = 0; j < i; j++)
83 if (tmp == fourcc[j])
84 goto err_out_duplicated;
85 fourcc[i] = tmp;
86 map[i] = alias ? mmioFOURCC(alias[0], alias[1], alias[2], alias[3]) : tmp;
87 s += 4;
88 i++;
89 } while ((*(s++) == ',') && --freeslots);
91 if (!freeslots)
92 goto err_out_too_many;
93 if (*(--s) != '\0')
94 goto err_out_parse_error;
95 return 1;
96 err_out_duplicated:
97 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"duplicated FourCC");
98 return 0;
99 err_out_too_many:
100 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"too many FourCCs/formats...");
101 return 0;
102 err_out_parse_error:
103 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"parse error");
104 return 0;
107 static int add_to_format(char *s, char *alias,unsigned int *fourcc, unsigned int *fourccmap)
109 int i, j;
110 char *endptr;
112 /* find first unused slot */
113 for (i = 0; i < CODECS_MAX_FOURCC && fourcc[i] != 0xffffffff; i++)
114 /* NOTHING */;
115 if (i == CODECS_MAX_FOURCC) {
116 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"too many FourCCs/formats...");
117 return 0;
120 fourcc[i]=strtoul(s,&endptr,0);
121 if (*endptr != '\0') {
122 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"parse error (format ID not a number?)");
123 return 0;
126 if(alias){
127 fourccmap[i]=strtoul(alias,&endptr,0);
128 if (*endptr != '\0') {
129 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"parse error (format ID alias not a number?)");
130 return 0;
132 } else
133 fourccmap[i]=fourcc[i];
135 for (j = 0; j < i; j++)
136 if (fourcc[j] == fourcc[i]) {
137 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"duplicated format ID");
138 return 0;
141 return 1;
144 static int add_to_inout(char *sfmt, char *sflags, unsigned int *outfmt,
145 unsigned char *outflags)
148 static char *flagstr[] = {
149 "flip",
150 "noflip",
151 "yuvhack",
152 "query",
153 "static",
154 NULL
157 int i, j, freeslots;
158 unsigned char flags;
160 for (i = 0; i < CODECS_MAX_OUTFMT && outfmt[i] != 0xffffffff; i++)
161 /* NOTHING */;
162 freeslots = CODECS_MAX_OUTFMT - i;
163 if (!freeslots)
164 goto err_out_too_many;
166 flags = 0;
167 if(sflags) {
168 do {
169 for (j = 0; flagstr[j] != NULL; j++)
170 if (!strncmp(sflags, flagstr[j],
171 strlen(flagstr[j])))
172 break;
173 if (flagstr[j] == NULL)
174 goto err_out_parse_error;
175 flags|=(1<<j);
176 sflags+=strlen(flagstr[j]);
177 } while (*(sflags++) == ',');
179 if (*(--sflags) != '\0')
180 goto err_out_parse_error;
183 do {
184 for (j = 0; isalnum(sfmt[j]) || sfmt[j] == '_'; j++);
185 unsigned int fmt = imgfmt_parse((struct bstr){sfmt, j}, true);
186 if (!fmt)
187 goto err_out_parse_error;
188 outfmt[i] = fmt;
189 outflags[i] = flags;
190 ++i;
191 sfmt += j;
192 } while ((*(sfmt++) == ',') && --freeslots);
194 if (!freeslots)
195 goto err_out_too_many;
197 if (*(--sfmt) != '\0')
198 goto err_out_parse_error;
200 return 1;
201 err_out_too_many:
202 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"too many out...");
203 return 0;
204 err_out_parse_error:
205 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"parse error");
206 return 0;
209 static int validate_codec(codecs_t *c, int type)
211 unsigned int i;
212 char *tmp_name = c->name;
214 for (i = 0; i < strlen(tmp_name) && isalnum(tmp_name[i]); i++)
215 /* NOTHING */;
217 if (i < strlen(tmp_name)) {
218 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"\ncodec(%s) name is not valid!\n", c->name);
219 return 0;
222 if (!c->info)
223 c->info = strdup(c->name);
225 if (!c->drv) {
226 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"\ncodec(%s) does not have a driver!\n", c->name);
227 return 0;
230 return 1;
233 static int add_comment(char *s, char **d)
235 int pos;
237 if (!*d)
238 pos = 0;
239 else {
240 pos = strlen(*d);
241 (*d)[pos++] = '\n';
243 if (!(*d = realloc(*d, pos + strlen(s) + 1))) {
244 mp_tmsg(MSGT_CODECCFG,MSGL_FATAL,"Can't allocate memory for comment. ");
245 return 0;
247 strcpy(*d + pos, s);
248 return 1;
251 static struct bstr filetext;
252 static int line_num = 0;
253 static char *line;
254 static char *token[MAX_NR_TOKEN];
255 static int read_nextline = 1;
257 static int get_token(int min, int max)
259 static int line_pos;
260 int i;
261 char c;
263 if (max >= MAX_NR_TOKEN) {
264 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"get_token(): max >= MAX_MR_TOKEN!");
265 goto out_eof;
268 memset(token, 0x00, sizeof(*token) * max);
270 if (read_nextline) {
271 if (!filetext.len)
272 goto out_eof;
273 struct bstr nextline = bstr_getline(filetext, &filetext);
274 line = nextline.start;
275 line[nextline.len - 1] = 0;
276 line_pos = 0;
277 ++line_num;
278 read_nextline = 0;
280 for (i = 0; i < max; i++) {
281 while (isspace(line[line_pos]))
282 ++line_pos;
283 if (line[line_pos] == '\0' || line[line_pos] == '#' ||
284 line[line_pos] == ';') {
285 read_nextline = 1;
286 if (i >= min)
287 goto out_ok;
288 goto out_eol;
290 token[i] = line + line_pos;
291 c = line[line_pos];
292 if (c == '"' || c == '\'') {
293 token[i]++;
294 while (line[++line_pos] != c && line[line_pos])
295 /* NOTHING */;
296 } else {
297 for (/* NOTHING */; !isspace(line[line_pos]) &&
298 line[line_pos]; line_pos++)
299 /* NOTHING */;
301 if (!line[line_pos]) {
302 read_nextline = 1;
303 if (i >= min - 1)
304 goto out_ok;
305 goto out_eol;
307 line[line_pos] = '\0';
308 line_pos++;
310 out_ok:
311 return i;
312 out_eof:
313 read_nextline = 1;
314 return RET_EOF;
315 out_eol:
316 return RET_EOL;
319 static codecs_t *video_codecs=NULL;
320 static codecs_t *audio_codecs=NULL;
321 static int nr_vcodecs = 0;
322 static int nr_acodecs = 0;
324 int parse_codec_cfg(const char *cfgfile, struct MPOpts *opts)
326 codecs_t *codec = NULL; // current codec
327 codecs_t **codecsp = NULL;// points to audio_codecs or to video_codecs
328 char *endptr; // strtoul()...
329 int *nr_codecsp;
330 int codec_type; /* TYPE_VIDEO/TYPE_AUDIO */
331 int tmp, i;
332 int codec_cfg_min;
334 for (struct bstr s = builtin_codecs_conf; ; bstr_getline(s, &s)) {
335 if (!s.len)
336 abort();
337 if (bstr_eatstart0(&s, "release ")) {
338 codec_cfg_min = atoi(s.start);
339 break;
343 // in case we call it a second time
344 codecs_uninit_free();
346 nr_vcodecs = 0;
347 nr_acodecs = 0;
349 if (cfgfile) {
350 // Avoid printing errors from open_stream when trying optional files
351 if (!mp_path_exists(cfgfile)) {
352 mp_tmsg(MSGT_CODECCFG, MSGL_V,
353 "No optional codecs config file: %s\n", cfgfile);
354 return 0;
356 mp_msg(MSGT_CODECCFG, MSGL_V, "Reading codec config file: %s\n",
357 cfgfile);
358 struct stream *s = open_stream(cfgfile, opts, NULL);
359 if (!s)
360 return 0;
361 filetext = stream_read_complete(s, NULL, 10000000, 1);
362 free_stream(s);
363 if (!filetext.start)
364 return 0;
365 } else
366 // Parsing modifies the data
367 filetext = bstrdup(NULL, builtin_codecs_conf);
368 void *tmpmem = filetext.start;
370 read_nextline = 1;
373 * this only catches release lines at the start of
374 * codecs.conf, before audiocodecs and videocodecs.
376 while ((tmp = get_token(1, 1)) == RET_EOL)
377 /* NOTHING */;
378 if (tmp == RET_EOF)
379 goto out;
380 if (!strcmp(token[0], "release")) {
381 if (get_token(1, 2) < 0)
382 goto err_out_parse_error;
383 tmp = atoi(token[0]);
384 if (tmp < codec_cfg_min)
385 goto err_out_release_num;
386 codecs_conf_release = tmp;
387 while ((tmp = get_token(1, 1)) == RET_EOL)
388 /* NOTHING */;
389 if (tmp == RET_EOF)
390 goto out;
391 } else
392 goto err_out_release_num;
395 * check if the next block starts with 'audiocodec' or
396 * with 'videocodec'
398 if (!strcmp(token[0], "audiocodec") || !strcmp(token[0], "videocodec"))
399 goto loop_enter;
400 goto err_out_parse_error;
402 while ((tmp = get_token(1, 1)) != RET_EOF) {
403 if (tmp == RET_EOL)
404 continue;
405 if (!strcmp(token[0], "audiocodec") ||
406 !strcmp(token[0], "videocodec")) {
407 if (!validate_codec(codec, codec_type))
408 goto err_out_not_valid;
409 loop_enter:
410 if (*token[0] == 'v') {
411 codec_type = TYPE_VIDEO;
412 nr_codecsp = &nr_vcodecs;
413 codecsp = &video_codecs;
414 } else {
415 assert(*token[0] == 'a');
416 codec_type = TYPE_AUDIO;
417 nr_codecsp = &nr_acodecs;
418 codecsp = &audio_codecs;
420 if (!(*codecsp = realloc(*codecsp,
421 sizeof(codecs_t) * (*nr_codecsp + 2)))) {
422 mp_tmsg(MSGT_CODECCFG,MSGL_FATAL,"Can't realloc '*codecsp': %s\n", strerror(errno));
423 goto err_out;
425 codec=*codecsp + *nr_codecsp;
426 ++*nr_codecsp;
427 memset(codec,0,sizeof(codecs_t));
428 memset(codec->fourcc, 0xff, sizeof(codec->fourcc));
429 memset(codec->outfmt, 0xff, sizeof(codec->outfmt));
430 memset(codec->infmt, 0xff, sizeof(codec->infmt));
432 if (get_token(1, 1) < 0)
433 goto err_out_parse_error;
434 for (i = 0; i < *nr_codecsp - 1; i++) {
435 if(( (*codecsp)[i].name!=NULL) &&
436 (!strcmp(token[0], (*codecsp)[i].name)) ) {
437 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"Codec name '%s' isn't unique.", token[0]);
438 goto err_out_print_linenum;
441 if (!(codec->name = strdup(token[0]))) {
442 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"Can't strdup -> 'name': %s\n", strerror(errno));
443 goto err_out;
445 } else if (!strcmp(token[0], "info")) {
446 if (codec->info || get_token(1, 1) < 0)
447 goto err_out_parse_error;
448 if (!(codec->info = strdup(token[0]))) {
449 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"Can't strdup -> 'info': %s\n", strerror(errno));
450 goto err_out;
452 } else if (!strcmp(token[0], "comment")) {
453 if (get_token(1, 1) < 0)
454 goto err_out_parse_error;
455 add_comment(token[0], &codec->comment);
456 } else if (!strcmp(token[0], "fourcc")) {
457 if (get_token(1, 2) < 0)
458 goto err_out_parse_error;
459 if (!add_to_fourcc(token[0], token[1],
460 codec->fourcc,
461 codec->fourccmap))
462 goto err_out_print_linenum;
463 } else if (!strcmp(token[0], "format")) {
464 if (get_token(1, 2) < 0)
465 goto err_out_parse_error;
466 if (!add_to_format(token[0], token[1],
467 codec->fourcc,codec->fourccmap))
468 goto err_out_print_linenum;
469 } else if (!strcmp(token[0], "driver")) {
470 if (get_token(1, 1) < 0)
471 goto err_out_parse_error;
472 if (!(codec->drv = strdup(token[0]))) {
473 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"Can't strdup -> 'driver': %s\n", strerror(errno));
474 goto err_out;
476 } else if (!strcmp(token[0], "dll")) {
477 if (get_token(1, 1) < 0)
478 goto err_out_parse_error;
479 if (!(codec->dll = strdup(token[0]))) {
480 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"Can't strdup -> 'dll': %s", strerror(errno));
481 goto err_out;
483 } else if (!strcmp(token[0], "guid")) {
484 if (get_token(11, 11) < 0)
485 goto err_out_parse_error;
486 codec->guid.f1=strtoul(token[0],&endptr,0);
487 if ((*endptr != ',' || *(endptr + 1) != '\0') &&
488 *endptr != '\0')
489 goto err_out_parse_error;
490 codec->guid.f2=strtoul(token[1],&endptr,0);
491 if ((*endptr != ',' || *(endptr + 1) != '\0') &&
492 *endptr != '\0')
493 goto err_out_parse_error;
494 codec->guid.f3=strtoul(token[2],&endptr,0);
495 if ((*endptr != ',' || *(endptr + 1) != '\0') &&
496 *endptr != '\0')
497 goto err_out_parse_error;
498 for (i = 0; i < 8; i++) {
499 codec->guid.f4[i]=strtoul(token[i + 3],&endptr,0);
500 if ((*endptr != ',' || *(endptr + 1) != '\0') &&
501 *endptr != '\0')
502 goto err_out_parse_error;
504 } else if (!strcmp(token[0], "out")) {
505 if (get_token(1, 2) < 0)
506 goto err_out_parse_error;
507 if (!add_to_inout(token[0], token[1], codec->outfmt,
508 codec->outflags))
509 goto err_out_print_linenum;
510 } else if (!strcmp(token[0], "in")) {
511 if (get_token(1, 2) < 0)
512 goto err_out_parse_error;
513 if (!add_to_inout(token[0], token[1], codec->infmt,
514 codec->inflags))
515 goto err_out_print_linenum;
516 } else if (!strcmp(token[0], "flags")) {
517 if (get_token(1, 1) < 0)
518 goto err_out_parse_error;
519 if (!strcmp(token[0], "seekable"))
520 codec->flags |= CODECS_FLAG_SEEKABLE;
521 else if (!strcmp(token[0], "align16"))
522 codec->flags |= CODECS_FLAG_ALIGN16;
523 else
524 goto err_out_parse_error;
525 } else if (!strcmp(token[0], "status")) {
526 if (get_token(1, 1) < 0)
527 goto err_out_parse_error;
528 if (!strcasecmp(token[0], "working"))
529 codec->status = CODECS_STATUS_WORKING;
530 else if (!strcasecmp(token[0], "crashing"))
531 codec->status = CODECS_STATUS_NOT_WORKING;
532 else if (!strcasecmp(token[0], "untested"))
533 codec->status = CODECS_STATUS_UNTESTED;
534 else if (!strcasecmp(token[0], "buggy"))
535 codec->status = CODECS_STATUS_PROBLEMS;
536 else
537 goto err_out_parse_error;
538 } else if (!strcmp(token[0], "anyinput")) {
539 codec->anyinput = true;
540 } else
541 goto err_out_parse_error;
543 if (!validate_codec(codec, codec_type))
544 goto err_out_not_valid;
545 mp_tmsg(MSGT_CODECCFG, MSGL_V, "%d audio & %d video codecs\n", nr_acodecs,
546 nr_vcodecs);
547 if(video_codecs) video_codecs[nr_vcodecs].name = NULL;
548 if(audio_codecs) audio_codecs[nr_acodecs].name = NULL;
549 out:
550 talloc_free(tmpmem);
551 line=NULL;
552 return 1;
554 err_out_parse_error:
555 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"parse error");
556 err_out_print_linenum:
557 PRINT_LINENUM;
558 err_out:
559 codecs_uninit_free();
561 talloc_free(tmpmem);
562 line=NULL;
563 line_num = 0;
564 return 0;
565 err_out_not_valid:
566 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"Codec is not defined correctly.");
567 goto err_out_print_linenum;
568 err_out_release_num:
569 mp_tmsg(MSGT_CODECCFG,MSGL_ERR,"This codecs.conf is too old and incompatible with this MPlayer release!");
570 goto err_out_print_linenum;
573 static void codecs_free(codecs_t* codecs,int count) {
574 int i;
575 for ( i = 0; i < count; i++)
576 if ( codecs[i].name ) {
577 free(codecs[i].name);
578 free(codecs[i].info);
579 free(codecs[i].comment);
580 free(codecs[i].dll);
581 free(codecs[i].drv);
583 free(codecs);
586 void codecs_uninit_free(void) {
587 if (video_codecs)
588 codecs_free(video_codecs,nr_vcodecs);
589 video_codecs=NULL;
590 if (audio_codecs)
591 codecs_free(audio_codecs,nr_acodecs);
592 audio_codecs=NULL;
595 codecs_t *find_audio_codec(unsigned int fourcc, unsigned int *fourccmap,
596 codecs_t *start, int force)
598 return find_codec(fourcc, fourccmap, start, 1, force);
601 codecs_t *find_video_codec(unsigned int fourcc, unsigned int *fourccmap,
602 codecs_t *start, int force)
604 return find_codec(fourcc, fourccmap, start, 0, force);
607 struct codecs *find_codec(unsigned int fourcc, unsigned int *fourccmap,
608 codecs_t *start, int audioflag, int force)
610 struct codecs *c, *end;
612 if (audioflag) {
613 c = audio_codecs;
614 end = c + nr_acodecs;
615 } else {
616 c = video_codecs;
617 end = c + nr_vcodecs;
619 if (start)
620 c = start + 1; // actually starts from the next one after the given one
621 for (; c < end; c++) {
622 for (int j = 0; j < CODECS_MAX_FOURCC; j++) {
623 if (c->fourcc[j] == -1)
624 break;
625 if (c->fourcc[j] == fourcc) {
626 if (fourccmap)
627 *fourccmap = c->fourccmap[j];
628 return c;
631 if (c->anyinput || force)
632 return c;
634 return NULL;
637 void stringset_init(stringset_t *set) {
638 *set = calloc(1, sizeof(char *));
641 void stringset_free(stringset_t *set) {
642 int count = 0;
643 while ((*set)[count]) free((*set)[count++]);
644 free(*set);
645 *set = NULL;
648 void stringset_add(stringset_t *set, const char *str) {
649 int count = 0;
650 while ((*set)[count]) count++;
651 count++;
652 *set = realloc(*set, sizeof(char *) * (count + 1));
653 (*set)[count - 1] = strdup(str);
654 (*set)[count] = NULL;
657 int stringset_test(stringset_t *set, const char *str) {
658 stringset_t s;
659 for (s = *set; *s; s++)
660 if (strcmp(*s, str) == 0)
661 return 1;
662 return 0;
665 void list_codecs(int audioflag){
666 int i;
667 codecs_t *c;
669 if (audioflag) {
670 i = nr_acodecs;
671 c = audio_codecs;
672 mp_msg(MSGT_CODECCFG,MSGL_INFO,"ac: afm: status: info: [lib/dll]\n");
673 } else {
674 i = nr_vcodecs;
675 c = video_codecs;
676 mp_msg(MSGT_CODECCFG,MSGL_INFO,"vc: vfm: status: info: [lib/dll]\n");
678 if(!i) return;
679 for (/* NOTHING */; i--; c++) {
680 char* s="unknown ";
681 switch(c->status){
682 case CODECS_STATUS_WORKING: s="working ";break;
683 case CODECS_STATUS_PROBLEMS: s="problems";break;
684 case CODECS_STATUS_NOT_WORKING: s="crashing";break;
685 case CODECS_STATUS_UNTESTED: s="untested";break;
687 if(c->dll)
688 mp_msg(MSGT_CODECCFG,MSGL_INFO,"%-11s %-9s %s %s [%s]\n",c->name,c->drv,s,c->info,c->dll);
689 else
690 mp_msg(MSGT_CODECCFG,MSGL_INFO,"%-11s %-9s %s %s\n",c->name,c->drv,s,c->info);