avformat/mpjpeg: allow processing of MIME parts without Content-Length header
Fixes ticket 5023 Signed-off-by: Alex Agranovsky <alex@sighthound.com>
This commit is contained in:
		
							parent
							
								
									97b8db334a
								
							
						
					
					
						commit
						79103f2199
					
				@ -23,13 +23,30 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "avformat.h"
 | 
					#include "avformat.h"
 | 
				
			||||||
#include "internal.h"
 | 
					#include "internal.h"
 | 
				
			||||||
 | 
					#include "avio_internal.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct MPJPEGDemuxContext {
 | 
				
			||||||
 | 
					    char       *boundary;
 | 
				
			||||||
 | 
					    char       *searchstr;
 | 
				
			||||||
 | 
					    int         searchstr_len;
 | 
				
			||||||
 | 
					} MPJPEGDemuxContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void trim_right(char *p)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!p || !*p)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    char *end = p + strlen(p);
 | 
				
			||||||
 | 
					    while (end > p && av_isspace(*(end-1)))
 | 
				
			||||||
 | 
					        *(--end) = '\0';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int get_line(AVIOContext *pb, char *line, int line_size)
 | 
					static int get_line(AVIOContext *pb, char *line, int line_size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int i = ff_get_line(pb, line, line_size);
 | 
					    ff_get_line(pb, line, line_size);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (i > 1 && line[i - 2] == '\r')
 | 
					 | 
				
			||||||
        line[i - 2] = '\0';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (pb->error)
 | 
					    if (pb->error)
 | 
				
			||||||
        return pb->error;
 | 
					        return pb->error;
 | 
				
			||||||
@ -37,21 +54,11 @@ static int get_line(AVIOContext *pb, char *line, int line_size)
 | 
				
			|||||||
    if (pb->eof_reached)
 | 
					    if (pb->eof_reached)
 | 
				
			||||||
        return AVERROR_EOF;
 | 
					        return AVERROR_EOF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    trim_right(line);
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void trim_right(char* p)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    char *end;
 | 
					 | 
				
			||||||
    if (!p || !*p)
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    end = p + strlen(p) - 1;
 | 
					 | 
				
			||||||
    while (end != p && av_isspace(*end)) {
 | 
					 | 
				
			||||||
        *end = '\0';
 | 
					 | 
				
			||||||
        end--;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int split_tag_value(char **tag, char **value, char *line)
 | 
					static int split_tag_value(char **tag, char **value, char *line)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -86,12 +93,24 @@ static int split_tag_value(char **tag, char **value, char *line)
 | 
				
			|||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int parse_multipart_header(AVIOContext *pb, void *log_ctx);
 | 
					static int parse_multipart_header(AVIOContext *pb,
 | 
				
			||||||
 | 
					                                    int* size,
 | 
				
			||||||
 | 
					                                    const char* expected_boundary,
 | 
				
			||||||
 | 
					                                    void *log_ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mpjpeg_read_close(AVFormatContext *s)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    MPJPEGDemuxContext *mpjpeg = s->priv_data;
 | 
				
			||||||
 | 
					    av_freep(&mpjpeg->boundary);
 | 
				
			||||||
 | 
					    av_freep(&mpjpeg->searchstr);
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int mpjpeg_read_probe(AVProbeData *p)
 | 
					static int mpjpeg_read_probe(AVProbeData *p)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    AVIOContext *pb;
 | 
					    AVIOContext *pb;
 | 
				
			||||||
    int ret = 0;
 | 
					    int ret = 0;
 | 
				
			||||||
 | 
					    int size = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (p->buf_size < 2 || p->buf[0] != '-' || p->buf[1] != '-')
 | 
					    if (p->buf_size < 2 || p->buf[0] != '-' || p->buf[1] != '-')
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
@ -100,7 +119,7 @@ static int mpjpeg_read_probe(AVProbeData *p)
 | 
				
			|||||||
    if (!pb)
 | 
					    if (!pb)
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ret = (parse_multipart_header(pb, NULL)>0)?AVPROBE_SCORE_MAX:0;
 | 
					    ret = (parse_multipart_header(pb, &size, "--", NULL) > 0) ? AVPROBE_SCORE_MAX : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    av_free(pb);
 | 
					    av_free(pb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -110,14 +129,15 @@ static int mpjpeg_read_probe(AVProbeData *p)
 | 
				
			|||||||
static int mpjpeg_read_header(AVFormatContext *s)
 | 
					static int mpjpeg_read_header(AVFormatContext *s)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    AVStream *st;
 | 
					    AVStream *st;
 | 
				
			||||||
    char boundary[70 + 2 + 1];
 | 
					    char boundary[70 + 2 + 1] = {0};
 | 
				
			||||||
    int64_t pos = avio_tell(s->pb);
 | 
					    int64_t pos = avio_tell(s->pb);
 | 
				
			||||||
    int ret;
 | 
					    int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
    ret = get_line(s->pb, boundary, sizeof(boundary));
 | 
					        ret = get_line(s->pb, boundary, sizeof(boundary));
 | 
				
			||||||
    if (ret < 0)
 | 
					        if (ret < 0)
 | 
				
			||||||
        return ret;
 | 
					            return ret;
 | 
				
			||||||
 | 
					    } while (!boundary[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (strncmp(boundary, "--", 2))
 | 
					    if (strncmp(boundary, "--", 2))
 | 
				
			||||||
        return AVERROR_INVALIDDATA;
 | 
					        return AVERROR_INVALIDDATA;
 | 
				
			||||||
@ -147,11 +167,16 @@ static int parse_content_length(const char *value)
 | 
				
			|||||||
    return val;
 | 
					    return val;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int parse_multipart_header(AVIOContext *pb, void *log_ctx)
 | 
					static int parse_multipart_header(AVIOContext *pb,
 | 
				
			||||||
 | 
					                            int* size,
 | 
				
			||||||
 | 
					                            const char* expected_boundary,
 | 
				
			||||||
 | 
					                            void *log_ctx)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    char line[128];
 | 
					    char line[128];
 | 
				
			||||||
    int found_content_type = 0;
 | 
					    int found_content_type = 0;
 | 
				
			||||||
    int ret, size = -1;
 | 
					    int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *size = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // get the CRLF as empty string
 | 
					    // get the CRLF as empty string
 | 
				
			||||||
    ret = get_line(pb, line, sizeof(line));
 | 
					    ret = get_line(pb, line, sizeof(line));
 | 
				
			||||||
@ -161,14 +186,21 @@ static int parse_multipart_header(AVIOContext *pb, void *log_ctx)
 | 
				
			|||||||
    /* some implementation do not provide the required
 | 
					    /* some implementation do not provide the required
 | 
				
			||||||
     * initial CRLF (see rfc1341 7.2.1)
 | 
					     * initial CRLF (see rfc1341 7.2.1)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    if (!line[0]) {
 | 
					    while (!line[0]) {
 | 
				
			||||||
        ret = get_line(pb, line, sizeof(line));
 | 
					        ret = get_line(pb, line, sizeof(line));
 | 
				
			||||||
        if (ret < 0)
 | 
					        if (ret < 0)
 | 
				
			||||||
            return ret;
 | 
					            return ret;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (strncmp(line, "--", 2))
 | 
					    if (!av_strstart(line, expected_boundary, NULL)) {
 | 
				
			||||||
 | 
					        av_log(log_ctx,
 | 
				
			||||||
 | 
					            AV_LOG_ERROR,
 | 
				
			||||||
 | 
					            "Expected boundary '%s' not found, instead found a line of %zu bytes\n",
 | 
				
			||||||
 | 
					            expected_boundary,
 | 
				
			||||||
 | 
					            strlen(line));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return AVERROR_INVALIDDATA;
 | 
					        return AVERROR_INVALIDDATA;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while (!pb->eof_reached) {
 | 
					    while (!pb->eof_reached) {
 | 
				
			||||||
        char *tag, *value;
 | 
					        char *tag, *value;
 | 
				
			||||||
@ -191,42 +223,90 @@ static int parse_multipart_header(AVIOContext *pb, void *log_ctx)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if (!av_strcasecmp(tag, "Content-type")) {
 | 
					        if (!av_strcasecmp(tag, "Content-type")) {
 | 
				
			||||||
            if (av_strcasecmp(value, "image/jpeg")) {
 | 
					            if (av_strcasecmp(value, "image/jpeg")) {
 | 
				
			||||||
                if (log_ctx) {
 | 
					                av_log(log_ctx, AV_LOG_ERROR,
 | 
				
			||||||
                    av_log(log_ctx, AV_LOG_ERROR,
 | 
					 | 
				
			||||||
                           "Unexpected %s : %s\n",
 | 
					                           "Unexpected %s : %s\n",
 | 
				
			||||||
                           tag, value);
 | 
					                           tag, value);
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return AVERROR_INVALIDDATA;
 | 
					                return AVERROR_INVALIDDATA;
 | 
				
			||||||
            } else
 | 
					            } else
 | 
				
			||||||
                found_content_type = 1;
 | 
					                found_content_type = 1;
 | 
				
			||||||
        } else if (!av_strcasecmp(tag, "Content-Length")) {
 | 
					        } else if (!av_strcasecmp(tag, "Content-Length")) {
 | 
				
			||||||
            size = parse_content_length(value);
 | 
					            *size = parse_content_length(value);
 | 
				
			||||||
            if (size < 0)
 | 
					            if ( *size < 0 )
 | 
				
			||||||
                return size;
 | 
					                av_log(log_ctx, AV_LOG_WARNING,
 | 
				
			||||||
 | 
					                           "Invalid Content-Length value : %s\n",
 | 
				
			||||||
 | 
					                           value);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!found_content_type || size < 0) {
 | 
					    return found_content_type ? 0 : AVERROR_INVALIDDATA;
 | 
				
			||||||
        return AVERROR_INVALIDDATA;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return size;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt)
 | 
					static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    int size;
 | 
				
			||||||
    int ret;
 | 
					    int ret;
 | 
				
			||||||
    int size = parse_multipart_header(s->pb, s);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (size < 0)
 | 
					    MPJPEGDemuxContext *mpjpeg = s->priv_data;
 | 
				
			||||||
        return size;
 | 
					    if (mpjpeg->boundary == NULL) {
 | 
				
			||||||
 | 
					        mpjpeg->boundary = av_strdup("--");
 | 
				
			||||||
 | 
					        mpjpeg->searchstr = av_strdup("\r\n--");
 | 
				
			||||||
 | 
					        if (!mpjpeg->boundary || !mpjpeg->searchstr) {
 | 
				
			||||||
 | 
					            av_freep(&mpjpeg->boundary);
 | 
				
			||||||
 | 
					            av_freep(&mpjpeg->searchstr);
 | 
				
			||||||
 | 
					            return AVERROR(ENOMEM);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        mpjpeg->searchstr_len = strlen(mpjpeg->searchstr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ret = parse_multipart_header(s->pb, &size, mpjpeg->boundary, s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ret = av_get_packet(s->pb, pkt, size);
 | 
					 | 
				
			||||||
    if (ret < 0)
 | 
					    if (ret < 0)
 | 
				
			||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 0;
 | 
					    if (size > 0) {
 | 
				
			||||||
 | 
					        /* size has been provided to us in MIME header */
 | 
				
			||||||
 | 
					        ret = av_get_packet(s->pb, pkt, size);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        /* no size was given -- we read until the next boundary or end-of-file */
 | 
				
			||||||
 | 
					        int remaining = 0, len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const int read_chunk = 2048;
 | 
				
			||||||
 | 
					        av_init_packet(pkt);
 | 
				
			||||||
 | 
					        pkt->data = NULL;
 | 
				
			||||||
 | 
					        pkt->size = 0;
 | 
				
			||||||
 | 
					        pkt->pos  = avio_tell(s->pb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* we may need to return as much as all we've read back to the buffer */
 | 
				
			||||||
 | 
					        ffio_ensure_seekback(s->pb, read_chunk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while ((ret = av_append_packet(s->pb, pkt, read_chunk - remaining)) >= 0) {
 | 
				
			||||||
 | 
					            /* scan the new data */
 | 
				
			||||||
 | 
					            len = ret + remaining;
 | 
				
			||||||
 | 
					            char *start = pkt->data + pkt->size - len;
 | 
				
			||||||
 | 
					            do {
 | 
				
			||||||
 | 
					                if (!memcmp(start, mpjpeg->searchstr, mpjpeg->searchstr_len)) {
 | 
				
			||||||
 | 
					                    // got the boundary! rewind the stream
 | 
				
			||||||
 | 
					                    avio_seek(s->pb, -(len-2), SEEK_CUR);
 | 
				
			||||||
 | 
					                    pkt->size -= (len-2);
 | 
				
			||||||
 | 
					                    return pkt->size;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                len--;
 | 
				
			||||||
 | 
					                start++;
 | 
				
			||||||
 | 
					            } while (len >= mpjpeg->searchstr_len);
 | 
				
			||||||
 | 
					            remaining = len;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* error or EOF occurred */
 | 
				
			||||||
 | 
					        if (ret == AVERROR_EOF) {
 | 
				
			||||||
 | 
					            ret = pkt->size > 0 ? pkt->size : AVERROR_EOF;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            av_packet_unref(pkt);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AVInputFormat ff_mpjpeg_demuxer = {
 | 
					AVInputFormat ff_mpjpeg_demuxer = {
 | 
				
			||||||
@ -234,7 +314,9 @@ AVInputFormat ff_mpjpeg_demuxer = {
 | 
				
			|||||||
    .long_name         = NULL_IF_CONFIG_SMALL("MIME multipart JPEG"),
 | 
					    .long_name         = NULL_IF_CONFIG_SMALL("MIME multipart JPEG"),
 | 
				
			||||||
    .mime_type         = "multipart/x-mixed-replace",
 | 
					    .mime_type         = "multipart/x-mixed-replace",
 | 
				
			||||||
    .extensions        = "mjpg",
 | 
					    .extensions        = "mjpg",
 | 
				
			||||||
 | 
					    .priv_data_size    = sizeof(MPJPEGDemuxContext),
 | 
				
			||||||
    .read_probe        = mpjpeg_read_probe,
 | 
					    .read_probe        = mpjpeg_read_probe,
 | 
				
			||||||
    .read_header       = mpjpeg_read_header,
 | 
					    .read_header       = mpjpeg_read_header,
 | 
				
			||||||
    .read_packet       = mpjpeg_read_packet,
 | 
					    .read_packet       = mpjpeg_read_packet,
 | 
				
			||||||
 | 
					    .read_close        = mpjpeg_read_close
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user