Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6147 serge 1
/*
2
 * Multipart JPEG format
3
 * Copyright (c) 2015 Luca Barbato
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
 
22
#include "libavutil/avstring.h"
23
 
24
#include "avformat.h"
25
#include "internal.h"
26
 
27
static int get_line(AVIOContext *pb, char *line, int line_size)
28
{
29
    int i = ff_get_line(pb, line, line_size);
30
 
31
    if (i > 1 && line[i - 2] == '\r')
32
        line[i - 2] = '\0';
33
 
34
    if (pb->error)
35
        return pb->error;
36
 
37
    if (pb->eof_reached)
38
        return AVERROR_EOF;
39
 
40
    return 0;
41
}
42
 
43
static int split_tag_value(char **tag, char **value, char *line)
44
{
45
    char *p = line;
46
 
47
    while (*p != '\0' && *p != ':')
48
        p++;
49
    if (*p != ':')
50
        return AVERROR_INVALIDDATA;
51
 
52
    *p   = '\0';
53
    *tag = line;
54
 
55
    p++;
56
 
57
    while (av_isspace(*p))
58
        p++;
59
 
60
    *value = p;
61
 
62
    return 0;
63
}
64
 
65
static int check_content_type(char *line)
66
{
67
    char *tag, *value;
68
    int ret = split_tag_value(&tag, &value, line);
69
 
70
    if (ret < 0)
71
        return ret;
72
 
73
    if (av_strcasecmp(tag, "Content-type") ||
74
        av_strcasecmp(value, "image/jpeg"))
75
        return AVERROR_INVALIDDATA;
76
 
77
    return 0;
78
}
79
 
80
static int mpjpeg_read_probe(AVProbeData *p)
81
{
82
    AVIOContext *pb;
83
    char line[128] = { 0 };
84
    int ret = 0;
85
 
86
    if (p->buf_size < 2 || p->buf[0] != '-' || p->buf[1] != '-')
87
        return 0;
88
 
89
    pb = avio_alloc_context(p->buf, p->buf_size, 0, NULL, NULL, NULL, NULL);
90
    if (!pb)
91
        return AVERROR(ENOMEM);
92
 
93
    while (!pb->eof_reached) {
94
        ret = get_line(pb, line, sizeof(line));
95
        if (ret < 0)
96
            break;
97
 
98
        ret = check_content_type(line);
99
        if (!ret) {
100
            ret = AVPROBE_SCORE_MAX;
101
            break;
102
        }
103
    }
104
 
105
    av_free(pb);
106
 
107
    return ret;
108
}
109
 
110
static int mpjpeg_read_header(AVFormatContext *s)
111
{
112
    AVStream *st;
113
    char boundary[70 + 2 + 1];
114
    int64_t pos = avio_tell(s->pb);
115
    int ret;
116
 
117
 
118
    ret = get_line(s->pb, boundary, sizeof(boundary));
119
    if (ret < 0)
120
        return ret;
121
 
122
    if (strncmp(boundary, "--", 2))
123
        return AVERROR_INVALIDDATA;
124
 
125
    st = avformat_new_stream(s, NULL);
126
    if (!st)
127
        return AVERROR(ENOMEM);
128
 
129
    st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
130
    st->codec->codec_id   = AV_CODEC_ID_MJPEG;
131
 
132
    avpriv_set_pts_info(st, 60, 1, 25);
133
 
134
    avio_seek(s->pb, pos, SEEK_SET);
135
 
136
    return 0;
137
}
138
 
139
static int parse_content_length(const char *value)
140
{
141
    long int val = strtol(value, NULL, 10);
142
 
143
    if (val == LONG_MIN || val == LONG_MAX)
144
        return AVERROR(errno);
145
    if (val > INT_MAX)
146
        return AVERROR(ERANGE);
147
    return val;
148
}
149
 
150
static int parse_multipart_header(AVFormatContext *s)
151
{
152
    char line[128];
153
    int found_content_type = 0;
154
    int ret, size = -1;
155
 
156
    ret = get_line(s->pb, line, sizeof(line));
157
    if (ret < 0)
158
        return ret;
159
 
160
    if (strncmp(line, "--", 2))
161
        return AVERROR_INVALIDDATA;
162
 
163
    while (!s->pb->eof_reached) {
164
        char *tag, *value;
165
 
166
        ret = get_line(s->pb, line, sizeof(line));
167
        if (ret < 0)
168
            return ret;
169
 
170
        if (line[0] == '\0')
171
            break;
172
 
173
        ret = split_tag_value(&tag, &value, line);
174
        if (ret < 0)
175
            return ret;
176
 
177
        if (!av_strcasecmp(tag, "Content-type")) {
178
            if (av_strcasecmp(value, "image/jpeg")) {
179
                av_log(s, AV_LOG_ERROR,
180
                       "Unexpected %s : %s\n",
181
                       tag, value);
182
                return AVERROR_INVALIDDATA;
183
            } else
184
                found_content_type = 1;
185
        } else if (!av_strcasecmp(tag, "Content-Length")) {
186
            size = parse_content_length(value);
187
            if (size < 0)
188
                return size;
189
        }
190
    }
191
 
192
    if (!found_content_type || size < 0) {
193
        return AVERROR_INVALIDDATA;
194
    }
195
 
196
    return size;
197
}
198
 
199
static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt)
200
{
201
    int ret;
202
    int size = parse_multipart_header(s);
203
 
204
    if (size < 0)
205
        return size;
206
 
207
    ret = av_get_packet(s->pb, pkt, size);
208
    if (ret < 0)
209
        return ret;
210
 
211
    // trailing empty line
212
    avio_skip(s->pb, 2);
213
 
214
    return 0;
215
}
216
 
217
AVInputFormat ff_mpjpeg_demuxer = {
218
    .name              = "mpjpeg",
219
    .long_name         = NULL_IF_CONFIG_SMALL("MIME multipart JPEG"),
220
    .mime_type         = "multipart/x-mixed-replace",
221
    .extensions        = "mjpg",
222
    .read_probe        = mpjpeg_read_probe,
223
    .read_header       = mpjpeg_read_header,
224
    .read_packet       = mpjpeg_read_packet,
225
};