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.

255 lines
7.4KB

  1. /*
  2. * This file is part of FFmpeg.
  3. *
  4. * FFmpeg is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2.1 of the License, or (at your option) any later version.
  8. *
  9. * FFmpeg is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with FFmpeg; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. */
  18. #include "config.h"
  19. #include <stdint.h>
  20. #include <string.h>
  21. #include <VideoToolbox/VideoToolbox.h>
  22. #include "buffer.h"
  23. #include "common.h"
  24. #include "hwcontext.h"
  25. #include "hwcontext_internal.h"
  26. #include "hwcontext_videotoolbox.h"
  27. #include "mem.h"
  28. #include "pixfmt.h"
  29. #include "pixdesc.h"
  30. static const struct {
  31. uint32_t cv_fmt;
  32. bool full_range;
  33. enum AVPixelFormat pix_fmt;
  34. } cv_pix_fmts[] = {
  35. { kCVPixelFormatType_420YpCbCr8Planar, false, AV_PIX_FMT_YUV420P },
  36. { kCVPixelFormatType_422YpCbCr8, false, AV_PIX_FMT_UYVY422 },
  37. { kCVPixelFormatType_32BGRA, false, AV_PIX_FMT_BGRA },
  38. #ifdef kCFCoreFoundationVersionNumber10_7
  39. { kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, false, AV_PIX_FMT_NV12 },
  40. { kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, true, AV_PIX_FMT_NV12 },
  41. #endif
  42. #if HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE
  43. { kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange, false, AV_PIX_FMT_P010 },
  44. { kCVPixelFormatType_420YpCbCr10BiPlanarFullRange, true, AV_PIX_FMT_P010 },
  45. #endif
  46. };
  47. enum AVPixelFormat av_map_videotoolbox_format_to_pixfmt(uint32_t cv_fmt)
  48. {
  49. int i;
  50. for (i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) {
  51. if (cv_pix_fmts[i].cv_fmt == cv_fmt)
  52. return cv_pix_fmts[i].pix_fmt;
  53. }
  54. return AV_PIX_FMT_NONE;
  55. }
  56. uint32_t av_map_videotoolbox_format_from_pixfmt(enum AVPixelFormat pix_fmt)
  57. {
  58. return av_map_videotoolbox_format_from_pixfmt2(pix_fmt, false);
  59. }
  60. uint32_t av_map_videotoolbox_format_from_pixfmt2(enum AVPixelFormat pix_fmt, bool full_range)
  61. {
  62. int i;
  63. for (i = 0; i < FF_ARRAY_ELEMS(cv_pix_fmts); i++) {
  64. if (cv_pix_fmts[i].pix_fmt == pix_fmt && cv_pix_fmts[i].full_range == full_range)
  65. return cv_pix_fmts[i].cv_fmt;
  66. }
  67. return 0;
  68. }
  69. static int vt_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
  70. {
  71. frame->buf[0] = av_buffer_pool_get(ctx->pool);
  72. if (!frame->buf[0])
  73. return AVERROR(ENOMEM);
  74. frame->data[3] = frame->buf[0]->data;
  75. frame->format = AV_PIX_FMT_VIDEOTOOLBOX;
  76. frame->width = ctx->width;
  77. frame->height = ctx->height;
  78. return 0;
  79. }
  80. static int vt_transfer_get_formats(AVHWFramesContext *ctx,
  81. enum AVHWFrameTransferDirection dir,
  82. enum AVPixelFormat **formats)
  83. {
  84. enum AVPixelFormat *fmts = av_malloc_array(2, sizeof(*fmts));
  85. if (!fmts)
  86. return AVERROR(ENOMEM);
  87. fmts[0] = ctx->sw_format;
  88. fmts[1] = AV_PIX_FMT_NONE;
  89. *formats = fmts;
  90. return 0;
  91. }
  92. static void vt_unmap(AVHWFramesContext *ctx, HWMapDescriptor *hwmap)
  93. {
  94. CVPixelBufferRef pixbuf = (CVPixelBufferRef)hwmap->source->data[3];
  95. CVPixelBufferUnlockBaseAddress(pixbuf, (uintptr_t)hwmap->priv);
  96. }
  97. static int vt_map_frame(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src,
  98. int flags)
  99. {
  100. CVPixelBufferRef pixbuf = (CVPixelBufferRef)src->data[3];
  101. OSType pixel_format = CVPixelBufferGetPixelFormatType(pixbuf);
  102. CVReturn err;
  103. uint32_t map_flags = 0;
  104. int ret;
  105. int i;
  106. enum AVPixelFormat format;
  107. format = av_map_videotoolbox_format_to_pixfmt(pixel_format);
  108. if (dst->format != format) {
  109. av_log(ctx, AV_LOG_ERROR, "Unsupported or mismatching pixel format: %s\n",
  110. av_fourcc2str(pixel_format));
  111. return AVERROR_UNKNOWN;
  112. }
  113. if (CVPixelBufferGetWidth(pixbuf) != ctx->width ||
  114. CVPixelBufferGetHeight(pixbuf) != ctx->height) {
  115. av_log(ctx, AV_LOG_ERROR, "Inconsistent frame dimensions.\n");
  116. return AVERROR_UNKNOWN;
  117. }
  118. if (flags == AV_HWFRAME_MAP_READ)
  119. map_flags = kCVPixelBufferLock_ReadOnly;
  120. err = CVPixelBufferLockBaseAddress(pixbuf, map_flags);
  121. if (err != kCVReturnSuccess) {
  122. av_log(ctx, AV_LOG_ERROR, "Error locking the pixel buffer.\n");
  123. return AVERROR_UNKNOWN;
  124. }
  125. if (CVPixelBufferIsPlanar(pixbuf)) {
  126. int planes = CVPixelBufferGetPlaneCount(pixbuf);
  127. for (i = 0; i < planes; i++) {
  128. dst->data[i] = CVPixelBufferGetBaseAddressOfPlane(pixbuf, i);
  129. dst->linesize[i] = CVPixelBufferGetBytesPerRowOfPlane(pixbuf, i);
  130. }
  131. } else {
  132. dst->data[0] = CVPixelBufferGetBaseAddress(pixbuf);
  133. dst->linesize[0] = CVPixelBufferGetBytesPerRow(pixbuf);
  134. }
  135. ret = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, vt_unmap,
  136. (void *)(uintptr_t)map_flags);
  137. if (ret < 0)
  138. goto unlock;
  139. return 0;
  140. unlock:
  141. CVPixelBufferUnlockBaseAddress(pixbuf, map_flags);
  142. return ret;
  143. }
  144. static int vt_transfer_data_from(AVHWFramesContext *hwfc,
  145. AVFrame *dst, const AVFrame *src)
  146. {
  147. AVFrame *map;
  148. int err;
  149. if (dst->width > hwfc->width || dst->height > hwfc->height)
  150. return AVERROR(EINVAL);
  151. map = av_frame_alloc();
  152. if (!map)
  153. return AVERROR(ENOMEM);
  154. map->format = dst->format;
  155. err = vt_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
  156. if (err)
  157. goto fail;
  158. map->width = dst->width;
  159. map->height = dst->height;
  160. err = av_frame_copy(dst, map);
  161. if (err)
  162. goto fail;
  163. err = 0;
  164. fail:
  165. av_frame_free(&map);
  166. return err;
  167. }
  168. static int vt_transfer_data_to(AVHWFramesContext *hwfc,
  169. AVFrame *dst, const AVFrame *src)
  170. {
  171. AVFrame *map;
  172. int err;
  173. if (src->width > hwfc->width || src->height > hwfc->height)
  174. return AVERROR(EINVAL);
  175. map = av_frame_alloc();
  176. if (!map)
  177. return AVERROR(ENOMEM);
  178. map->format = src->format;
  179. err = vt_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
  180. if (err)
  181. goto fail;
  182. map->width = src->width;
  183. map->height = src->height;
  184. err = av_frame_copy(map, src);
  185. if (err)
  186. goto fail;
  187. err = 0;
  188. fail:
  189. av_frame_free(&map);
  190. return err;
  191. }
  192. static int vt_device_create(AVHWDeviceContext *ctx, const char *device,
  193. AVDictionary *opts, int flags)
  194. {
  195. if (device && device[0]) {
  196. av_log(ctx, AV_LOG_ERROR, "Device selection unsupported.\n");
  197. return AVERROR_UNKNOWN;
  198. }
  199. return 0;
  200. }
  201. const HWContextType ff_hwcontext_type_videotoolbox = {
  202. .type = AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
  203. .name = "videotoolbox",
  204. .device_create = vt_device_create,
  205. .frames_get_buffer = vt_get_buffer,
  206. .transfer_get_formats = vt_transfer_get_formats,
  207. .transfer_data_to = vt_transfer_data_to,
  208. .transfer_data_from = vt_transfer_data_from,
  209. .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NONE },
  210. };