avinfo.c 13.7 KB
Newer Older
1
2
3
4
5
/* -*- tab-width:4;c-file-style:"cc-mode"; -*- */
/*
 * ffmpeg2theora.c -- Convert ffmpeg supported a/v files to Ogg Theora / Ogg Vorbis
 * Copyright (C) 2003-2008 <j@v2v.cc>
 *
6
 * gcc -o avinfo avinfo.c -DAVINFO `pkg-config --cflags --libs libavcodec libavformat`
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

24
25
26
27
28
29
30
31
32
33
34
35
36
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#if !defined(_LARGEFILE_SOURCE)
#define _LARGEFILE_SOURCE
#endif
#if !defined(_LARGEFILE64_SOURCE)
#define _LARGEFILE64_SOURCE
#endif
#if !defined(_FILE_OFFSET_BITS)
#define _FILE_OFFSET_BITS 64
#endif

37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <math.h>
#include <errno.h>
#include <sys/stat.h>

#include "libavformat/avformat.h"

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#if !defined(off64_t)
#define off64_t off_t
#endif

#ifdef WIN32
#define fseeko fseeko64
#define ftello ftello64
#endif

#ifdef _BIG_ENDIAN
#define htonll(x) \
((((x) & 0xff00000000000000LL) >> 56) | \
(((x) & 0x00ff000000000000LL) >> 40) | \
(((x) & 0x0000ff0000000000LL) >> 24) | \
(((x) & 0x000000ff00000000LL) >> 8) | \
(((x) & 0x00000000ff000000LL) << 8) | \
(((x) & 0x0000000000ff0000LL) << 24) | \
(((x) & 0x000000000000ff00LL) << 40) | \
(((x) & 0x00000000000000ffLL) << 56))
#else
#define htonll(x) x
#endif

unsigned long long get_filesize(char const *filename) {
    unsigned long long size = 0;
    FILE *file = fopen(filename, "rb");
    if (file) {
        fseeko(file, 0, SEEK_END);
        size = ftello(file);
77
    }
78
79
    fclose(file);
    return size;
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
}

char const *fix_codec_name(char const *codec_name) {
    if (!strcmp(codec_name, "libschrodinger")) {
        codec_name = "dirac";
    }
    else if (!strcmp(codec_name, "vp6f")) {
        codec_name = "vp6";
    }
    else if (!strcmp(codec_name, "mpeg2video")) {
        codec_name = "mpeg2";
    }
    else if (!strcmp(codec_name, "mpeg1video")) {
        codec_name = "mpeg1";
    }
    else if (!strcmp(codec_name, "0x0000")) {
        codec_name = "mu-law";
    }
   return codec_name;
}

Jan Gerber's avatar
Jan Gerber committed
101
102
103
104
105
106
107
108
109
110
111
112
113
114
char *replace_str(char *str, char *orig, char *rep) {
  char buffer[4096];
  char *p, *p2, *rest;

  if(!(p = strstr(str, orig)))
    return str;

  strncpy(buffer, str, p-str);
  buffer[p-str] = '\0';
  rest = replace_str(p+strlen(orig), orig, rep);
  sprintf(buffer+(p-str), "%s%s", rep, rest);
  return buffer;
}

115
116
117
enum {
    JSON_STRING,
    JSON_INT,
118
    JSON_LONGLONG,
119
120
121
    JSON_FLOAT,
} JSON_TYPES;

Jan Gerber's avatar
Jan Gerber committed
122
void json_add_key_value(FILE *output, char *key, void *value, int type, int last) {
Jan Gerber's avatar
Jan Gerber committed
123
    char *p, *pp;
124
125
    switch(type) {
        case JSON_STRING:
Jan Gerber's avatar
Jan Gerber committed
126
            p = (char *)value;
Jan Gerber's avatar
Jan Gerber committed
127
128
129
            p = replace_str(p, "\\", "\\\\");
            p = replace_str(p, "\"", "\\\"");
            fprintf(output, "  \"%s\": \"%s\"", key, p);
130
131
            break;
        case JSON_INT:
Jan Gerber's avatar
Jan Gerber committed
132
            fprintf(output, "  \"%s\": %d", key, *(int *)value);
133
            break;
134
135
        case JSON_LONGLONG:
            fprintf(output, "  \"%s\": %lld", key, *(unsigned long long *)value);
Jan Gerber's avatar
Jan Gerber committed
136
            break;
137
        case JSON_FLOAT:
Jan Gerber's avatar
Jan Gerber committed
138
            fprintf(output, "  \"%s\": %f", key, *(float *)value);
139
140
            break;
    }
Jan Gerber's avatar
Jan Gerber committed
141
142
143
144
145
    if (last) {
        fprintf(output, "\n");
    } else {
        fprintf(output, ",\n");
    }
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
}

void json_codec_info(FILE *output, AVCodecContext *enc) {
    const char *codec_name;
    AVCodec *p;
    char buf1[32];
    int bitrate;
    AVRational display_aspect_ratio;

    p = avcodec_find_decoder(enc->codec_id);

    if (p) {
        codec_name = p->name;
    } else if (enc->codec_id == CODEC_ID_MPEG2TS) {
        /* fake mpeg2 transport stream codec (currently not
           registered) */
        codec_name = "mpeg2ts";
    } else if (enc->codec_name[0] != '\0') {
        codec_name = enc->codec_name;
    } else {
        /* output avi tags */
        if(   isprint(enc->codec_tag&0xFF) && isprint((enc->codec_tag>>8)&0xFF)
           && isprint((enc->codec_tag>>16)&0xFF) && isprint((enc->codec_tag>>24)&0xFF)){
            snprintf(buf1, sizeof(buf1), "%c%c%c%c / 0x%04X",
                     enc->codec_tag & 0xff,
                     (enc->codec_tag >> 8) & 0xff,
                     (enc->codec_tag >> 16) & 0xff,
                     (enc->codec_tag >> 24) & 0xff,
                      enc->codec_tag);
        } else {
            snprintf(buf1, sizeof(buf1), "0x%04x", enc->codec_tag);
        }
        codec_name = buf1;
    }

    switch(enc->codec_type) {
    case CODEC_TYPE_VIDEO:
        codec_name = fix_codec_name(codec_name);
Jan Gerber's avatar
Jan Gerber committed
184
        json_add_key_value(output, "video_codec", (void *)codec_name, JSON_STRING, 0);
185
        if (enc->pix_fmt != PIX_FMT_NONE) {
Jan Gerber's avatar
Jan Gerber committed
186
            json_add_key_value(output, "pixel_format", (void *)avcodec_get_pix_fmt_name(enc->pix_fmt), JSON_STRING, 0);
187
188
        }
        if (enc->width) {
Jan Gerber's avatar
Jan Gerber committed
189
190
            json_add_key_value(output, "width", &enc->width, JSON_INT, 0);
            json_add_key_value(output, "height", &enc->height, JSON_INT, 0);
191
192
193
194
195
196
197
            if (enc->sample_aspect_ratio.num) {
                av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
                          enc->width*enc->sample_aspect_ratio.num,
                          enc->height*enc->sample_aspect_ratio.den,
                          1024*1024);
                snprintf(buf1, sizeof(buf1), "%d:%d",
                         enc->sample_aspect_ratio.num, enc->sample_aspect_ratio.den);
Jan Gerber's avatar
Jan Gerber committed
198
                json_add_key_value(output, "pixel_aspect_ratio", buf1, JSON_STRING, 0);
199
200
                snprintf(buf1, sizeof(buf1), "%d:%d",
                         display_aspect_ratio.num, display_aspect_ratio.den);
Jan Gerber's avatar
Jan Gerber committed
201
                json_add_key_value(output, "display_aspect_ratio", buf1, JSON_STRING, 0);
202
203
204
205
206
            }
        }
        bitrate = enc->bit_rate;
        if (bitrate != 0) {
            float t = (float)bitrate / 1000;
Jan Gerber's avatar
Jan Gerber committed
207
            json_add_key_value(output, "video_bitrate", &t, JSON_FLOAT, 0);
208
209
210
211
        }
        break;
    case CODEC_TYPE_AUDIO:
        codec_name = fix_codec_name(codec_name);
Jan Gerber's avatar
Jan Gerber committed
212
        json_add_key_value(output, "audio_codec", (void *)codec_name, JSON_STRING, 0);
213
        if (enc->sample_rate) {
Jan Gerber's avatar
Jan Gerber committed
214
            json_add_key_value(output, "samplerate", &enc->sample_rate, JSON_INT, 0);
215
        }
Jan Gerber's avatar
Jan Gerber committed
216
        json_add_key_value(output, "channels", &enc->channels, JSON_INT, 0);
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258

        /* for PCM codecs, compute bitrate directly */
        switch(enc->codec_id) {
        case CODEC_ID_PCM_F64BE:
        case CODEC_ID_PCM_F64LE:
            bitrate = enc->sample_rate * enc->channels * 64;
            break;
        case CODEC_ID_PCM_S32LE:
        case CODEC_ID_PCM_S32BE:
        case CODEC_ID_PCM_U32LE:
        case CODEC_ID_PCM_U32BE:
        case CODEC_ID_PCM_F32BE:
        case CODEC_ID_PCM_F32LE:
            bitrate = enc->sample_rate * enc->channels * 32;
            break;
        case CODEC_ID_PCM_S24LE:
        case CODEC_ID_PCM_S24BE:
        case CODEC_ID_PCM_U24LE:
        case CODEC_ID_PCM_U24BE:
        case CODEC_ID_PCM_S24DAUD:
            bitrate = enc->sample_rate * enc->channels * 24;
            break;
        case CODEC_ID_PCM_S16LE:
        case CODEC_ID_PCM_S16BE:
        case CODEC_ID_PCM_S16LE_PLANAR:
        case CODEC_ID_PCM_U16LE:
        case CODEC_ID_PCM_U16BE:
            bitrate = enc->sample_rate * enc->channels * 16;
            break;
        case CODEC_ID_PCM_S8:
        case CODEC_ID_PCM_U8:
        case CODEC_ID_PCM_ALAW:
        case CODEC_ID_PCM_MULAW:
        case CODEC_ID_PCM_ZORK:
            bitrate = enc->sample_rate * enc->channels * 8;
            break;
        default:
            bitrate = enc->bit_rate;
            break;
        }
        if (bitrate != 0) {
            float t = (float)bitrate / 1000;
Jan Gerber's avatar
Jan Gerber committed
259
            json_add_key_value(output, "audio_bitrate", &t, JSON_FLOAT, 0);
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
        }
        break;
    /*
    case CODEC_TYPE_DATA:
        fprintf(output, "datacodec: %s\n", codec_name);
        bitrate = enc->bit_rate;
        break;
    case CODEC_TYPE_SUBTITLE:
        fprintf(output, "subtitle: %s\n", codec_name);
        bitrate = enc->bit_rate;
        break;
    case CODEC_TYPE_ATTACHMENT:
        fprintf(output, "attachment: : %s\n", codec_name);
        bitrate = enc->bit_rate;
        break;
    */
    default:
        //FIXME: ignore unkown for now
        /*
        snprintf(buf, buf_size, "Invalid Codec type %d", enc->codec_type);
        */
        return;
    }
}


static void json_stream_format(FILE *output, AVFormatContext *ic, int i) {
    char buf[1024];
    char buf1[32];
    int flags = ic->iformat->flags;
    AVStream *st = ic->streams[i];
    int g = av_gcd(st->time_base.num, st->time_base.den);
    AVMetadataTag *lang = av_metadata_get(st->metadata, "language", NULL, 0);
    json_codec_info(output, st->codec);
    if (st->sample_aspect_ratio.num && // default
        av_cmp_q(st->sample_aspect_ratio, st->codec->sample_aspect_ratio)) {
        AVRational display_aspect_ratio;
        av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
                  st->codec->width*st->sample_aspect_ratio.num,
                  st->codec->height*st->sample_aspect_ratio.den,
                  1024*1024);
        snprintf(buf1, sizeof(buf1), "%d:%d",
                 st->sample_aspect_ratio.num, st->sample_aspect_ratio.den);
Jan Gerber's avatar
Jan Gerber committed
303
        json_add_key_value(output, "pixel_aspect_ratio", buf1, JSON_STRING, 0);
304
305
        snprintf(buf1, sizeof(buf1), "%d:%d",
                 display_aspect_ratio.num, display_aspect_ratio.den);
Jan Gerber's avatar
Jan Gerber committed
306
        json_add_key_value(output, "display_aspect_ratio", buf1, JSON_STRING, 0);
307
308
309
310
311
    }
    if(st->codec->codec_type == CODEC_TYPE_VIDEO){
        if (st->time_base.den && st->time_base.num && av_q2d(st->time_base) > 0.001) {
            snprintf(buf1, sizeof(buf1), "%d:%d",
                     st->time_base.den, st->time_base.num);
Jan Gerber's avatar
Jan Gerber committed
312
            json_add_key_value(output, "framerate", buf1, JSON_STRING, 0);
313
314
315
        } else {
            snprintf(buf1, sizeof(buf1), "%d:%d",
                     st->r_frame_rate.num, st->r_frame_rate.den);
Jan Gerber's avatar
Jan Gerber committed
316
            json_add_key_value(output, "framerate", buf1, JSON_STRING, 0);
317
318
319
320
        }
    }
}

321
322
323
324
/* 
 * os hash
 * based on public domain example from
 * http://trac.opensubtitles.org/projects/opensubtitles/wiki/HashSourceCodes   
325
326
 * 
 * plus modification for files < 64k, buffer is filled with file data and padded with 0
327
 */
328

329
330
331
332
333
unsigned long long gen_oshash(char const *filename) {
    FILE *file;
    int i;
    unsigned long long t1=0;
    unsigned long long buffer1[8192*2];
Jan Gerber's avatar
Jan Gerber committed
334
    int used = 8192*2;
335
336
    file = fopen(filename, "rb");
    if (file) {
337
        //add filesize
338
339
340
341
        fseeko(file, 0, SEEK_END);
        t1 = ftello(file);
        fseeko(file, 0, SEEK_SET);

342
        if(t1 < 65536) {
Jan Gerber's avatar
Jan Gerber committed
343
344
            used = t1/8;
            fread(buffer1, used, 8, file);
345
346
        } else {
            fread(buffer1, 8192, 8, file);
347
            fseeko(file, -65536, SEEK_END);
348
349
            fread(&buffer1[8192], 8192, 8, file); 
        }
Jan Gerber's avatar
Jan Gerber committed
350
        for (i=0; i < used; i++)
351
            t1+=htonll(buffer1[i]);
352
353
354
355
356
357
        fclose(file);
    }
    return t1;
}

void json_oshash(FILE *output, char const *filename) {
Jan Gerber's avatar
Jan Gerber committed
358
    char hash[32];
Jan Gerber's avatar
Jan Gerber committed
359
#ifdef WIN32
360
    sprintf(hash,"%016I64x", gen_oshash(filename));
Jan Gerber's avatar
Jan Gerber committed
361
#else
362
    sprintf(hash,"%016qx", gen_oshash(filename));
Jan Gerber's avatar
Jan Gerber committed
363
#endif
364
365
366
367
    json_add_key_value(output, "oshash", (void *)hash, JSON_STRING, 0);
}


368
369
370
/* "user interface" functions */
void json_format_info(FILE* output, AVFormatContext *ic, const char *url) {
    int i;
371
    unsigned long long filesize;
372
373
374
375
376

    fprintf(output, "{\n");
    if (ic->duration != AV_NOPTS_VALUE) {
        float secs;
        secs = (float)ic->duration / AV_TIME_BASE;
Jan Gerber's avatar
Jan Gerber committed
377
        json_add_key_value(output, "duration", &secs, JSON_FLOAT, 0);
378
379
    } else {
        float t = -1;
Jan Gerber's avatar
Jan Gerber committed
380
        json_add_key_value(output, "duration", &t, JSON_FLOAT, 0);
381
382
383
    }
    if (ic->bit_rate) {
        float t = (float)ic->bit_rate / 1000;
Jan Gerber's avatar
Jan Gerber committed
384
        json_add_key_value(output, "bitrate", &t, JSON_FLOAT, 0);
385
386
387
388
389
390
391
392
393
394
395
396
397
    }

    if(ic->nb_programs) {
        int j, k;
        for(j=0; j<ic->nb_programs; j++) {
            for(k=0; k<ic->programs[j]->nb_stream_indexes; k++)
                json_stream_format(output, ic, ic->programs[j]->stream_index[k]);
         }
    } else {
        for(i=0;i<ic->nb_streams;i++) {
            json_stream_format(output, ic, i);
        }
    }
398
    json_oshash(output, url);
Jan Gerber's avatar
Jan Gerber committed
399
    json_add_key_value(output, "path", (void *)url, JSON_STRING, 0);
400

Jan Gerber's avatar
Jan Gerber committed
401
    filesize = get_filesize(url);
402
    json_add_key_value(output, "size", &filesize, JSON_LONGLONG, 1);
403

404
405
406
    fprintf(output, "}\n");
}

407
#ifdef AVINFO
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
int main(int argc, char **argv) {
    char inputfile_name[255];
    AVInputFormat *input_fmt = NULL;
    AVFormatParameters *formatParams = NULL;
    AVFormatContext *context;
    FILE* output = stdout;
    
    avcodec_register_all();
    av_register_all();

    if(argc == 1) {
        fprintf(stderr, "usage: %s avfile [outputfile]\n", argv[0]);
        exit(1);
    }
    snprintf(inputfile_name, sizeof(inputfile_name),"%s", argv[1]);
    if(argc == 3) {
        output = fopen(argv[2], "w");
    }
    
    if (av_open_input_file(&context, inputfile_name, input_fmt, 0, formatParams) >= 0) {
        if (av_find_stream_info(context) >= 0) {
            json_format_info(output, context, inputfile_name);
        }
    }
    if(output != stdout) {
        fclose(output);
    }
}
436
#endif