1
mirror of https://git.videolan.org/git/ffmpeg.git synced 2024-09-28 16:02:17 +02:00

avformat/mpjpeg: utilize MIME boundary value to detect start of new frame

This code is disabled by default so not to regress endpoints sending invalid MIME, but can be enabled via AVOption 'strict_mime_boundary'

Signed-off-by: Alex Agranovsky <alex@sighthound.com>
This commit is contained in:
Alex Agranovsky 2015-11-29 18:54:14 -05:00 committed by wm4
parent 79103f2199
commit 259c71c199
2 changed files with 84 additions and 3 deletions

View File

@ -450,6 +450,21 @@ to 1 (-1 means automatic setting, 1 means enabled, 0 means
disabled). Default value is -1.
@end table
@section mpjpeg
MJPEG encapsulated in multi-part MIME demuxer.
This demuxer allows reading of MJPEG, where each frame is represented as a part of
multipart/x-mixed-replace stream.
@table @option
@item strict_mime_boundary
Default implementation applies a relaxed standard to multi-part MIME boundary detection,
to prevent regression with numerous existing endpoints not generating a proper MIME
MJPEG stream. Turning this option on by setting it to 1 will result in a stricter check
of the boundary value.
@end table
@section rawvideo
Raw video demuxer.

View File

@ -20,6 +20,7 @@
*/
#include "libavutil/avstring.h"
#include "libavutil/opt.h"
#include "avformat.h"
#include "internal.h"
@ -28,9 +29,11 @@
typedef struct MPJPEGDemuxContext {
const AVClass *class;
char *boundary;
char *searchstr;
int searchstr_len;
int strict_mime_boundary;
} MPJPEGDemuxContext;
@ -242,6 +245,41 @@ static int parse_multipart_header(AVIOContext *pb,
}
static char* mpjpeg_get_boundary(AVIOContext* pb)
{
uint8_t *mime_type = NULL;
uint8_t *start;
uint8_t *end;
uint8_t *res = NULL;
int len;
/* get MIME type, and skip to the first parameter */
av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type);
start = mime_type;
while (start != NULL && *start != '\0') {
start = strchr(start, ';');
if (start)
start = start+1;
while (av_isspace(*start))
start++;
if (!av_stristart(start, "boundary=", &start)) {
end = strchr(start, ';');
if (end)
len = end - start - 1;
else
len = strlen(start);
res = av_strndup(start, len);
break;
}
}
av_freep(&mime_type);
return res;
}
static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt)
{
int size;
@ -249,8 +287,17 @@ static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt)
MPJPEGDemuxContext *mpjpeg = s->priv_data;
if (mpjpeg->boundary == NULL) {
mpjpeg->boundary = av_strdup("--");
mpjpeg->searchstr = av_strdup("\r\n--");
uint8_t* boundary = NULL;
if (mpjpeg->strict_mime_boundary) {
boundary = mpjpeg_get_boundary(s->pb);
}
if (boundary != NULL) {
mpjpeg->boundary = boundary;
mpjpeg->searchstr = av_asprintf( "\r\n%s\r\n", boundary );
} else {
mpjpeg->boundary = av_strdup("--");
mpjpeg->searchstr = av_strdup("\r\n--");
}
if (!mpjpeg->boundary || !mpjpeg->searchstr) {
av_freep(&mpjpeg->boundary);
av_freep(&mpjpeg->searchstr);
@ -309,6 +356,22 @@ static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt)
return ret;
}
#define OFFSET(x) offsetof(MPJPEGDemuxContext, x)
#define DEC AV_OPT_FLAG_DECODING_PARAM
const AVOption mpjpeg_options[] = {
{ "strict_mime_boundary", "require MIME boundaries match", OFFSET(strict_mime_boundary), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC },
{ NULL }
};
static const AVClass mpjpeg_demuxer_class = {
.class_name = "MPJPEG demuxer",
.item_name = av_default_item_name,
.option = mpjpeg_options,
.version = LIBAVUTIL_VERSION_INT,
};
AVInputFormat ff_mpjpeg_demuxer = {
.name = "mpjpeg",
.long_name = NULL_IF_CONFIG_SMALL("MIME multipart JPEG"),
@ -318,5 +381,8 @@ AVInputFormat ff_mpjpeg_demuxer = {
.read_probe = mpjpeg_read_probe,
.read_header = mpjpeg_read_header,
.read_packet = mpjpeg_read_packet,
.read_close = mpjpeg_read_close
.read_close = mpjpeg_read_close,
.priv_class = &mpjpeg_demuxer_class
};