You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

119 lines
3.3KB

  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. #include <string.h>
  21. #include "libavutil/avstring.h"
  22. #include "libavutil/base64.h"
  23. #include "url.h"
  24. typedef struct {
  25. const uint8_t *data;
  26. void *tofree;
  27. size_t size;
  28. size_t pos;
  29. } DataContext;
  30. static av_cold int data_open(URLContext *h, const char *uri, int flags)
  31. {
  32. DataContext *dc = h->priv_data;
  33. const char *data, *opt, *next;
  34. char *ddata;
  35. int ret, base64 = 0;
  36. size_t in_size;
  37. /* data:content/type[;base64],payload */
  38. av_strstart(uri, "data:", &uri);
  39. data = strchr(uri, ',');
  40. if (!data) {
  41. av_log(h, AV_LOG_ERROR, "No ',' delimiter in URI\n");
  42. return AVERROR(EINVAL);
  43. }
  44. opt = uri;
  45. while (opt < data) {
  46. next = av_x_if_null(memchr(opt, ';', data - opt), data);
  47. if (opt == uri) {
  48. if (!memchr(opt, '/', next - opt)) { /* basic validity check */
  49. av_log(h, AV_LOG_ERROR, "Invalid content-type '%.*s'\n",
  50. (int)(next - opt), opt);
  51. return AVERROR(EINVAL);
  52. }
  53. av_log(h, AV_LOG_VERBOSE, "Content-type: %.*s\n",
  54. (int)(next - opt), opt);
  55. } else {
  56. if (!av_strncasecmp(opt, "base64", next - opt)) {
  57. base64 = 1;
  58. } else {
  59. av_log(h, AV_LOG_VERBOSE, "Ignoring option '%.*s'\n",
  60. (int)(next - opt), opt);
  61. }
  62. }
  63. opt = next + 1;
  64. }
  65. data++;
  66. in_size = strlen(data);
  67. if (base64) {
  68. size_t out_size = 3 * (in_size / 4) + 1;
  69. if (out_size > INT_MAX || !(ddata = av_malloc(out_size)))
  70. return AVERROR(ENOMEM);
  71. if ((ret = av_base64_decode(ddata, data, out_size)) < 0) {
  72. av_free(ddata);
  73. av_log(h, AV_LOG_ERROR, "Invalid base64 in URI\n");
  74. return ret;
  75. }
  76. dc->data = dc->tofree = ddata;
  77. dc->size = ret;
  78. } else {
  79. dc->data = data;
  80. dc->size = in_size;
  81. }
  82. return 0;
  83. }
  84. static av_cold int data_close(URLContext *h)
  85. {
  86. DataContext *dc = h->priv_data;
  87. av_freep(&dc->tofree);
  88. return 0;
  89. }
  90. static int data_read(URLContext *h, unsigned char *buf, int size)
  91. {
  92. DataContext *dc = h->priv_data;
  93. if (dc->pos >= dc->size)
  94. return AVERROR_EOF;
  95. size = FFMIN(size, dc->size - dc->pos);
  96. memcpy(buf, dc->data + dc->pos, size);
  97. dc->pos += size;
  98. return size;
  99. }
  100. URLProtocol ff_data_protocol = {
  101. .name = "data",
  102. .url_open = data_open,
  103. .url_close = data_close,
  104. .url_read = data_read,
  105. .priv_data_size = sizeof(DataContext),
  106. };