ffmetadec.c 4.58 KB
Newer Older
Anton Khirnov's avatar
Anton Khirnov committed
1 2 3 4
/*
 * Metadata demuxer
 * Copyright (c) 2010 Anton Khirnov
 *
5
 * This file is part of Libav.
Anton Khirnov's avatar
Anton Khirnov committed
6
 *
7
 * Libav is free software; you can redistribute it and/or
Anton Khirnov's avatar
Anton Khirnov committed
8 9 10 11
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
12
 * Libav is distributed in the hope that it will be useful,
Anton Khirnov's avatar
Anton Khirnov committed
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with Libav; if not, write to the Free Software
Anton Khirnov's avatar
Anton Khirnov committed
19 20 21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

22
#include "libavutil/mathematics.h"
Anton Khirnov's avatar
Anton Khirnov committed
23
#include "avformat.h"
24
#include "ffmeta.h"
25
#include "internal.h"
26
#include "libavutil/dict.h"
Anton Khirnov's avatar
Anton Khirnov committed
27 28 29 30 31 32 33 34

static int probe(AVProbeData *p)
{
    if(!memcmp(p->buf, ID_STRING, strlen(ID_STRING)))
        return AVPROBE_SCORE_MAX;
    return 0;
}

35
static void get_line(AVIOContext *s, uint8_t *buf, int size)
Anton Khirnov's avatar
Anton Khirnov committed
36 37 38 39 40
{
    do {
        uint8_t c;
        int i = 0;

41
        while ((c = avio_r8(s))) {
Anton Khirnov's avatar
Anton Khirnov committed
42 43 44
            if (c == '\\') {
                if (i < size - 1)
                    buf[i++] = c;
45
                c = avio_r8(s);
Anton Khirnov's avatar
Anton Khirnov committed
46 47 48 49 50 51 52
            } else if (c == '\n')
                break;

            if (i < size - 1)
                buf[i++] = c;
        }
        buf[i] = 0;
Anton Khirnov's avatar
Anton Khirnov committed
53
    } while (!s->eof_reached && (buf[0] == ';' || buf[0] == '#' || buf[0] == 0));
Anton Khirnov's avatar
Anton Khirnov committed
54 55 56 57 58 59 60 61 62 63 64 65
}

static AVChapter *read_chapter(AVFormatContext *s)
{
    uint8_t line[256];
    int64_t start, end;
    AVRational tb = {1, 1e9};

    get_line(s->pb, line, sizeof(line));

    if (sscanf(line, "TIMEBASE=%d/%d", &tb.num, &tb.den))
        get_line(s->pb, line, sizeof(line));
66
    if (!sscanf(line, "START=%"SCNd64, &start)) {
Anton Khirnov's avatar
Anton Khirnov committed
67 68 69 70 71 72
        av_log(s, AV_LOG_ERROR, "Expected chapter start timestamp, found %s.\n", line);
        start = (s->nb_chapters && s->chapters[s->nb_chapters - 1]->end != AV_NOPTS_VALUE) ?
                 s->chapters[s->nb_chapters - 1]->end : 0;
    } else
        get_line(s->pb, line, sizeof(line));

73
    if (!sscanf(line, "END=%"SCNd64, &end)) {
Anton Khirnov's avatar
Anton Khirnov committed
74 75 76 77
        av_log(s, AV_LOG_ERROR, "Expected chapter end timestamp, found %s.\n", line);
        end = AV_NOPTS_VALUE;
    }

78
    return avpriv_new_chapter(s, s->nb_chapters, tb, start, end, NULL);
Anton Khirnov's avatar
Anton Khirnov committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
}

static uint8_t *unescape(uint8_t *buf, int size)
{
    uint8_t *ret = av_malloc(size + 1);
    uint8_t *p1  = ret, *p2 = buf;

    if (!ret)
        return NULL;

    while (p2 < buf + size) {
        if (*p2 == '\\')
            p2++;
        *p1++ = *p2++;
    }
    *p1 = 0;
    return ret;
}

98
static int read_tag(uint8_t *line, AVDictionary **m)
Anton Khirnov's avatar
Anton Khirnov committed
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
{
    uint8_t *key, *value, *p = line;

    /* find first not escaped '=' */
    while (1) {
        if (*p == '=')
            break;
        else if (*p == '\\')
            p++;

        if (*p++)
            continue;

        return 0;
    }

    if (!(key = unescape(line, p - line)))
        return AVERROR(ENOMEM);
    if (!(value = unescape(p + 1, strlen(p + 1)))) {
        av_free(key);
        return AVERROR(ENOMEM);
    }

122
    av_dict_set(m, key, value, AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
Anton Khirnov's avatar
Anton Khirnov committed
123 124 125
    return 0;
}

126
static int read_header(AVFormatContext *s)
Anton Khirnov's avatar
Anton Khirnov committed
127
{
128
    AVDictionary **m = &s->metadata;
Anton Khirnov's avatar
Anton Khirnov committed
129 130
    uint8_t line[1024];

Anton Khirnov's avatar
Anton Khirnov committed
131
    while(!s->pb->eof_reached) {
Anton Khirnov's avatar
Anton Khirnov committed
132 133 134
        get_line(s->pb, line, sizeof(line));

        if (!memcmp(line, ID_STREAM, strlen(ID_STREAM))) {
135
            AVStream *st = avformat_new_stream(s, NULL);
Anton Khirnov's avatar
Anton Khirnov committed
136 137 138 139 140

            if (!st)
                return -1;

            st->codec->codec_type = AVMEDIA_TYPE_DATA;
141
            st->codec->codec_id   = AV_CODEC_ID_FFMETADATA;
Anton Khirnov's avatar
Anton Khirnov committed
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

            m = &st->metadata;
        } else if (!memcmp(line, ID_CHAPTER, strlen(ID_CHAPTER))) {
            AVChapter *ch = read_chapter(s);

            if (!ch)
                return -1;

            m = &ch->metadata;
        } else
            read_tag(line, m);
    }

    s->start_time = 0;
    if (s->nb_chapters)
        s->duration = av_rescale_q(s->chapters[s->nb_chapters - 1]->end,
                                   s->chapters[s->nb_chapters - 1]->time_base,
                                   AV_TIME_BASE_Q);

    return 0;
}

static int read_packet(AVFormatContext *s, AVPacket *pkt)
{
    return AVERROR_EOF;
}

169
AVInputFormat ff_ffmetadata_demuxer = {
Anton Khirnov's avatar
Anton Khirnov committed
170
    .name        = "ffmetadata",
171
    .long_name   = NULL_IF_CONFIG_SMALL("FFmpeg metadata in text"),
Anton Khirnov's avatar
Anton Khirnov committed
172 173 174 175
    .read_probe  = probe,
    .read_header = read_header,
    .read_packet = read_packet,
};