2 * Multipart JPEG format
3 * Copyright (c) 2015 Luca Barbato
5 * This file is part of FFmpeg.
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.
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.
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
22 #include "libavutil/avstring.h"
23 #include "libavutil/opt.h"
27 #include "avio_internal.h"
31 typedef struct MPJPEGDemuxContext
{
36 int strict_mime_boundary
;
40 static void trim_right(char *p
)
48 while (end
> p
&& av_isspace(*(end
-1)))
52 static int get_line(AVIOContext
*pb
, char *line
, int line_size
)
54 ff_get_line(pb
, line
, line_size
);
68 static int split_tag_value(char **tag
, char **value
, char *line
)
77 while (*p
!= '\0' && *p
!= ':') {
78 if (!av_isspace(*p
)) {
84 return foundData
? AVERROR_INVALIDDATA
: 0;
92 while (av_isspace(*p
))
101 static int parse_multipart_header(AVIOContext
*pb
,
103 const char* expected_boundary
,
106 static int mpjpeg_read_close(AVFormatContext
*s
)
108 MPJPEGDemuxContext
*mpjpeg
= s
->priv_data
;
109 av_freep(&mpjpeg
->boundary
);
110 av_freep(&mpjpeg
->searchstr
);
114 static int mpjpeg_read_probe(const AVProbeData
*p
)
120 if (p
->buf_size
< 2 || p
->buf
[0] != '-' || p
->buf
[1] != '-')
123 ffio_init_context(&pb
, p
->buf
, p
->buf_size
, 0, NULL
, NULL
, NULL
, NULL
);
125 ret
= (parse_multipart_header(&pb
, &size
, "--", NULL
) >= 0) ? AVPROBE_SCORE_MAX
: 0;
130 static int mpjpeg_read_header(AVFormatContext
*s
)
133 char boundary
[70 + 2 + 1] = {0};
134 int64_t pos
= avio_tell(s
->pb
);
138 ret
= get_line(s
->pb
, boundary
, sizeof(boundary
));
141 } while (!boundary
[0]);
143 if (strncmp(boundary
, "--", 2))
144 return AVERROR_INVALIDDATA
;
146 st
= avformat_new_stream(s
, NULL
);
148 return AVERROR(ENOMEM
);
150 st
->codecpar
->codec_type
= AVMEDIA_TYPE_VIDEO
;
151 st
->codecpar
->codec_id
= AV_CODEC_ID_MJPEG
;
153 avpriv_set_pts_info(st
, 60, 1, 25);
155 avio_seek(s
->pb
, pos
, SEEK_SET
);
160 static int parse_content_length(const char *value
)
162 long int val
= strtol(value
, NULL
, 10);
164 if (val
== LONG_MIN
|| val
== LONG_MAX
)
165 return AVERROR(errno
);
167 return AVERROR(ERANGE
);
171 static int parse_multipart_header(AVIOContext
*pb
,
173 const char* expected_boundary
,
177 int found_content_type
= 0;
182 // get the CRLF as empty string
183 ret
= get_line(pb
, line
, sizeof(line
));
187 /* some implementation do not provide the required
188 * initial CRLF (see rfc1341 7.2.1)
191 ret
= get_line(pb
, line
, sizeof(line
));
196 if (!av_strstart(line
, expected_boundary
, NULL
)) {
200 "Expected boundary '%s' not found, instead found a line of %"SIZE_SPECIFIER
" bytes\n",
204 return AVERROR_INVALIDDATA
;
207 while (!pb
->eof_reached
) {
210 ret
= get_line(pb
, line
, sizeof(line
));
212 if (ret
== AVERROR_EOF
)
220 ret
= split_tag_value(&tag
, &value
, line
);
223 if (value
==NULL
|| tag
==NULL
)
226 if (!av_strcasecmp(tag
, "Content-type")) {
227 if (av_strcasecmp(value
, "image/jpeg")) {
229 av_log(log_ctx
, AV_LOG_ERROR
,
230 "Unexpected %s : %s\n",
232 return AVERROR_INVALIDDATA
;
234 found_content_type
= 1;
235 } else if (!av_strcasecmp(tag
, "Content-Length")) {
236 *size
= parse_content_length(value
);
239 av_log(log_ctx
, AV_LOG_WARNING
,
240 "Invalid Content-Length value : %s\n",
245 return found_content_type
? 0 : AVERROR_INVALIDDATA
;
249 static char* mpjpeg_get_boundary(AVIOContext
* pb
)
251 uint8_t *mime_type
= NULL
;
257 /* get MIME type, and skip to the first parameter */
258 av_opt_get(pb
, "mime_type", AV_OPT_SEARCH_CHILDREN
, &mime_type
);
260 while (start
!= NULL
&& *start
!= '\0') {
261 start
= strchr(start
, ';');
267 while (av_isspace(*start
))
270 if (av_stristart(start
, "boundary=", &start
)) {
271 end
= strchr(start
, ';');
273 len
= end
- start
- 1;
277 /* some endpoints may enclose the boundary
278 in Content-Type in quotes */
279 if ( len
>2 && *start
== '"' && start
[len
-1] == '"' ) {
283 res
= av_strndup(start
, len
);
288 av_freep(&mime_type
);
293 static int mpjpeg_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
298 MPJPEGDemuxContext
*mpjpeg
= s
->priv_data
;
299 if (mpjpeg
->boundary
== NULL
) {
300 uint8_t* boundary
= NULL
;
301 if (mpjpeg
->strict_mime_boundary
) {
302 boundary
= mpjpeg_get_boundary(s
->pb
);
304 if (boundary
!= NULL
) {
305 mpjpeg
->boundary
= av_asprintf("--%s", boundary
);
306 mpjpeg
->searchstr
= av_asprintf("\r\n--%s\r\n", boundary
);
309 mpjpeg
->boundary
= av_strdup("--");
310 mpjpeg
->searchstr
= av_strdup("\r\n--");
312 if (!mpjpeg
->boundary
|| !mpjpeg
->searchstr
) {
313 av_freep(&mpjpeg
->boundary
);
314 av_freep(&mpjpeg
->searchstr
);
315 return AVERROR(ENOMEM
);
317 mpjpeg
->searchstr_len
= strlen(mpjpeg
->searchstr
);
320 ret
= parse_multipart_header(s
->pb
, &size
, mpjpeg
->boundary
, s
);
327 /* size has been provided to us in MIME header */
328 ret
= av_get_packet(s
->pb
, pkt
, size
);
330 /* no size was given -- we read until the next boundary or end-of-file */
331 int remaining
= 0, len
;
333 const int read_chunk
= 2048;
335 pkt
->pos
= avio_tell(s
->pb
);
337 while ((ret
= ffio_ensure_seekback(s
->pb
, read_chunk
- remaining
)) >= 0 && /* we may need to return as much as all we've read back to the buffer */
338 (ret
= av_append_packet(s
->pb
, pkt
, read_chunk
- remaining
)) >= 0) {
339 /* scan the new data */
342 len
= ret
+ remaining
;
343 start
= pkt
->data
+ pkt
->size
- len
;
345 if (!memcmp(start
, mpjpeg
->searchstr
, mpjpeg
->searchstr_len
)) {
346 // got the boundary! rewind the stream
347 avio_seek(s
->pb
, -len
, SEEK_CUR
);
353 } while (len
>= mpjpeg
->searchstr_len
);
357 /* error or EOF occurred */
358 if (ret
== AVERROR_EOF
) {
359 ret
= pkt
->size
> 0 ? pkt
->size
: AVERROR_EOF
;
366 #define OFFSET(x) offsetof(MPJPEGDemuxContext, x)
368 #define DEC AV_OPT_FLAG_DECODING_PARAM
369 static const AVOption mpjpeg_options
[] = {
370 { "strict_mime_boundary", "require MIME boundaries match", OFFSET(strict_mime_boundary
), AV_OPT_TYPE_BOOL
, {.i64
= 0}, 0, 1, DEC
},
375 static const AVClass mpjpeg_demuxer_class
= {
376 .class_name
= "MPJPEG demuxer",
377 .item_name
= av_default_item_name
,
378 .option
= mpjpeg_options
,
379 .version
= LIBAVUTIL_VERSION_INT
,
382 AVInputFormat ff_mpjpeg_demuxer
= {
384 .long_name
= NULL_IF_CONFIG_SMALL("MIME multipart JPEG"),
385 .mime_type
= "multipart/x-mixed-replace",
386 .extensions
= "mjpg",
387 .priv_data_size
= sizeof(MPJPEGDemuxContext
),
388 .read_probe
= mpjpeg_read_probe
,
389 .read_header
= mpjpeg_read_header
,
390 .read_packet
= mpjpeg_read_packet
,
391 .read_close
= mpjpeg_read_close
,
392 .priv_class
= &mpjpeg_demuxer_class
,
393 .flags
= AVFMT_NOTIMESTAMPS
,