Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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. };
  226.