1/* 2 * Copyright (c) 2012 Nicolas George 3 * 4 * This file is part of FFmpeg. 5 * 6 * FFmpeg is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public License 8 * as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * FFmpeg is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public License 17 * along with FFmpeg; if not, write to the Free Software Foundation, Inc., 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21#include <string.h> 22#include "libavutil/avstring.h" 23#include "libavutil/base64.h" 24#include "url.h" 25 26typedef struct { 27 const uint8_t *data; 28 void *tofree; 29 size_t size; 30 size_t pos; 31} DataContext; 32 33static av_cold int data_open(URLContext *h, const char *uri, int flags) 34{ 35 DataContext *dc = h->priv_data; 36 const char *data, *opt, *next; 37 char *ddata; 38 int ret, base64 = 0; 39 size_t in_size; 40 41 /* data:content/type[;base64],payload */ 42 43 av_strstart(uri, "data:", &uri); 44 data = strchr(uri, ','); 45 if (!data) { 46 av_log(h, AV_LOG_ERROR, "No ',' delimiter in URI\n"); 47 return AVERROR(EINVAL); 48 } 49 opt = uri; 50 while (opt < data) { 51 next = av_x_if_null(memchr(opt, ';', data - opt), data); 52 if (opt == uri) { 53 if (!memchr(opt, '/', next - opt)) { /* basic validity check */ 54 av_log(h, AV_LOG_ERROR, "Invalid content-type '%.*s'\n", 55 (int)(next - opt), opt); 56 return AVERROR(EINVAL); 57 } 58 av_log(h, AV_LOG_VERBOSE, "Content-type: %.*s\n", 59 (int)(next - opt), opt); 60 } else { 61 if (!av_strncasecmp(opt, "base64", next - opt)) { 62 base64 = 1; 63 } else { 64 av_log(h, AV_LOG_VERBOSE, "Ignoring option '%.*s'\n", 65 (int)(next - opt), opt); 66 } 67 } 68 opt = next + 1; 69 } 70 71 data++; 72 in_size = strlen(data); 73 if (base64) { 74 size_t out_size = 3 * (in_size / 4) + 1; 75 76 if (out_size > INT_MAX || !(ddata = av_malloc(out_size))) 77 return AVERROR(ENOMEM); 78 if ((ret = av_base64_decode(ddata, data, out_size)) < 0) { 79 av_free(ddata); 80 av_log(h, AV_LOG_ERROR, "Invalid base64 in URI\n"); 81 return ret; 82 } 83 dc->data = dc->tofree = ddata; 84 dc->size = ret; 85 } else { 86 dc->data = data; 87 dc->size = in_size; 88 } 89 return 0; 90} 91 92static av_cold int data_close(URLContext *h) 93{ 94 DataContext *dc = h->priv_data; 95 96 av_freep(&dc->tofree); 97 return 0; 98} 99 100static int data_read(URLContext *h, unsigned char *buf, int size) 101{ 102 DataContext *dc = h->priv_data; 103 104 if (dc->pos >= dc->size) 105 return AVERROR_EOF; 106 size = FFMIN(size, dc->size - dc->pos); 107 memcpy(buf, dc->data + dc->pos, size); 108 dc->pos += size; 109 return size; 110} 111 112URLProtocol ff_data_protocol = { 113 .name = "data", 114 .url_open = data_open, 115 .url_close = data_close, 116 .url_read = data_read, 117 .priv_data_size = sizeof(DataContext), 118}; 119