Adds the new av_hwframe_map() function, which allows mapping between hardware frames and normal memory, along with internal support for implementing it. Also adds av_hwframe_ctx_create_derived(), for creating a hardware frames context associated with one device using frames mapped from another by some hardware-specific means.
Unified · Split
| @@ -13,6 +13,10 @@ libavutil: 2015-08-28 · 12 unmodified lines | |||
| 13 | 13 | ||
| 14 | API changes, most recent first: | 14 | API changes, most recent first: |
| 15 | 15 | ||
| 16 | 2016-xx-xx - xxxxxxx - lavu 55.27.0 - hwcontext.h | ||
| 17 | Add av_hwframe_map() and associated AV_HWFRAME_MAP_* flags. | ||
| 18 | Add av_hwframe_ctx_create_derived(). | ||
| 19 | |||
| 16 | 2016-xx-xx - xxxxxxx - lavu 55.25.0 - pixfmt.h | 20 | 2016-xx-xx - xxxxxxx - lavu 55.25.0 - pixfmt.h |
| 17 | Add AV_PIX_FMT_GBRAP12(LE/BE). | 21 | Add AV_PIX_FMT_GBRAP12(LE/BE). |
| 18 | 22 | ||
| @@ -157,14 +157,19 @@ static void hwframe_ctx_free(void *opaque, uint8_t *data) · 156 unmodified lines | |||
| 157 | { | 157 | { |
| 158 | AVHWFramesContext *ctx = (AVHWFramesContext*)data; | 158 | AVHWFramesContext *ctx = (AVHWFramesContext*)data; |
| 159 | 159 | ||
| 160 | if (ctx->internal->pool_internal) | 160 | if (ctx->internal->source_frames) { |
| 161 | av_buffer_pool_uninit(&ctx->internal->pool_internal); | 161 | av_buffer_unref(&ctx->internal->source_frames); |
| 162 | 162 | ||
| 163 | if (ctx->internal->hw_type->frames_uninit) | 163 | } else { |
| 164 | ctx->internal->hw_type->frames_uninit(ctx); | 164 | if (ctx->internal->pool_internal) |
| 165 | av_buffer_pool_uninit(&ctx->internal->pool_internal); | ||
| 165 | 166 | ||
| 166 | if (ctx->free) | 167 | if (ctx->internal->hw_type->frames_uninit) |
| 167 | ctx->free(ctx); | 168 | ctx->internal->hw_type->frames_uninit(ctx); |
| 169 | |||
| 170 | if (ctx->free) | ||
| 171 | ctx->free(ctx); | ||
| 172 | } | ||
| 168 | 173 | ||
| 169 | av_buffer_unref(&ctx->device_ref); | 174 | av_buffer_unref(&ctx->device_ref); |
| 170 | 175 | ||
| @@ -266,6 +271,11 @@ int av_hwframe_ctx_init(AVBufferRef *ref) · 95 unmodified lines | |||
| 266 | const enum AVPixelFormat *pix_fmt; | 271 | const enum AVPixelFormat *pix_fmt; |
| 267 | int ret; | 272 | int ret; |
| 268 | 273 | ||
| 274 | if (ctx->internal->source_frames) { | ||
| 275 | /* A derived frame context is already initialised. */ | ||
| 276 | return 0; | ||
| 277 | } | ||
| 278 | |||
| 269 | /* validate the pixel format */ | 279 | /* validate the pixel format */ |
| 270 | for (pix_fmt = ctx->internal->hw_type->pix_fmts; *pix_fmt != AV_PIX_FMT_NONE; pix_fmt++) { | 280 | for (pix_fmt = ctx->internal->hw_type->pix_fmts; *pix_fmt != AV_PIX_FMT_NONE; pix_fmt++) { |
| 271 | if (*pix_fmt == ctx->format) | 281 | if (*pix_fmt == ctx->format) |
| @@ -396,6 +406,35 @@ int av_hwframe_get_buffer(AVBufferRef *hwframe_ref, AVFrame *frame, int flags) · 124 unmodified lines | |||
| 396 | AVHWFramesContext *ctx = (AVHWFramesContext*)hwframe_ref->data; | 406 | AVHWFramesContext *ctx = (AVHWFramesContext*)hwframe_ref->data; |
| 397 | int ret; | 407 | int ret; |
| 398 | 408 | ||
| 409 | if (ctx->internal->source_frames) { | ||
| 410 | // This is a derived frame context, so we allocate in the source | ||
| 411 | // and map the frame immediately. | ||
| 412 | AVFrame *src_frame; | ||
| 413 | |||
| 414 | src_frame = av_frame_alloc(); | ||
| 415 | if (!src_frame) | ||
| 416 | return AVERROR(ENOMEM); | ||
| 417 | |||
| 418 | ret = av_hwframe_get_buffer(ctx->internal->source_frames, | ||
| 419 | src_frame, 0); | ||
| 420 | if (ret < 0) | ||
| 421 | return ret; | ||
| 422 | |||
| 423 | ret = av_hwframe_map(frame, src_frame, 0); | ||
| 424 | if (ret) { | ||
| 425 | av_log(ctx, AV_LOG_ERROR, "Failed to map frame into derived " | ||
| 426 | "frame context: %d.\n", ret); | ||
| 427 | av_frame_free(&src_frame); | ||
| 428 | return ret; | ||
| 429 | } | ||
| 430 | |||
| 431 | // Free the source frame immediately - the mapped frame still | ||
| 432 | // contains a reference to it. | ||
| 433 | av_frame_free(&src_frame); | ||
| 434 | |||
| 435 | return 0; | ||
| 436 | } | ||
| 437 | |||
| 399 | if (!ctx->internal->hw_type->frames_get_buffer) | 438 | if (!ctx->internal->hw_type->frames_get_buffer) |
| 400 | return AVERROR(ENOSYS); | 439 | return AVERROR(ENOSYS); |
| 401 | 440 | ||
| @@ -495,3 +534,191 @@ fail: · 93 unmodified lines | |||
| 495 | *pdevice_ref = NULL; | 534 | *pdevice_ref = NULL; |
| 496 | return ret; | 535 | return ret; |
| 497 | } | 536 | } |
| 537 | |||
| 538 | static void ff_hwframe_unmap(void *opaque, uint8_t *data) | ||
| 539 | { | ||
| 540 | HWMapDescriptor *hwmap = (HWMapDescriptor*)data; | ||
| 541 | AVHWFramesContext *ctx = opaque; | ||
| 542 | |||
| 543 | if (hwmap->unmap) | ||
| 544 | hwmap->unmap(ctx, hwmap); | ||
| 545 | |||
| 546 | av_frame_free(&hwmap->source); | ||
| 547 | |||
| 548 | av_buffer_unref(&hwmap->hw_frames_ctx); | ||
| 549 | |||
| 550 | av_free(hwmap); | ||
| 551 | } | ||
| 552 | |||
| 553 | int ff_hwframe_map_create(AVBufferRef *hwframe_ref, | ||
| 554 | AVFrame *dst, const AVFrame *src, | ||
| 555 | void (*unmap)(AVHWFramesContext *ctx, | ||
| 556 | HWMapDescriptor *hwmap), | ||
| 557 | void *priv) | ||
| 558 | { | ||
| 559 | AVHWFramesContext *ctx = (AVHWFramesContext*)hwframe_ref->data; | ||
| 560 | HWMapDescriptor *hwmap; | ||
| 561 | int ret; | ||
| 562 | |||
| 563 | hwmap = av_mallocz(sizeof(*hwmap)); | ||
| 564 | if (!hwmap) { | ||
| 565 | ret = AVERROR(ENOMEM); | ||
| 566 | goto fail; | ||
| 567 | } | ||
| 568 | |||
| 569 | hwmap->source = av_frame_alloc(); | ||
| 570 | if (!hwmap->source) { | ||
| 571 | ret = AVERROR(ENOMEM); | ||
| 572 | goto fail; | ||
| 573 | } | ||
| 574 | ret = av_frame_ref(hwmap->source, src); | ||
| 575 | if (ret < 0) | ||
| 576 | goto fail; | ||
| 577 | |||
| 578 | hwmap->hw_frames_ctx = av_buffer_ref(hwframe_ref); | ||
| 579 | if (!hwmap->hw_frames_ctx) { | ||
| 580 | ret = AVERROR(ENOMEM); | ||
| 581 | goto fail; | ||
| 582 | } | ||
| 583 | |||
| 584 | hwmap->unmap = unmap; | ||
| 585 | hwmap->priv = priv; | ||
| 586 | |||
| 587 | dst->buf[0] = av_buffer_create((uint8_t*)hwmap, sizeof(*hwmap), | ||
| 588 | &ff_hwframe_unmap, ctx, 0); | ||
| 589 | if (!dst->buf[0]) { | ||
| 590 | ret = AVERROR(ENOMEM); | ||
| 591 | goto fail; | ||
| 592 | } | ||
| 593 | |||
| 594 | return 0; | ||
| 595 | |||
| 596 | fail: | ||
| 597 | if (hwmap) { | ||
| 598 | av_buffer_unref(&hwmap->hw_frames_ctx); | ||
| 599 | av_frame_free(&hwmap->source); | ||
| 600 | } | ||
| 601 | av_free(hwmap); | ||
| 602 | return ret; | ||
| 603 | } | ||
| 604 | |||
| 605 | int av_hwframe_map(AVFrame *dst, const AVFrame *src, int flags) | ||
| 606 | { | ||
| 607 | AVHWFramesContext *src_frames, *dst_frames; | ||
| 608 | HWMapDescriptor *hwmap; | ||
| 609 | int ret; | ||
| 610 | |||
| 611 | if (src->hw_frames_ctx && dst->hw_frames_ctx) { | ||
| 612 | src_frames = (AVHWFramesContext*)src->hw_frames_ctx->data; | ||
| 613 | dst_frames = (AVHWFramesContext*)dst->hw_frames_ctx->data; | ||
| 614 | |||
| 615 | if ((src_frames == dst_frames && | ||
| 616 | src->format == dst_frames->sw_format && | ||
| 617 | dst->format == dst_frames->format) || | ||
| 618 | (src_frames->internal->source_frames && | ||
| 619 | src_frames->internal->source_frames->data == | ||
| 620 | (uint8_t*)dst_frames)) { | ||
| 621 | // This is an unmap operation. We don't need to directly | ||
| 622 | // do anything here other than fill in the original frame, | ||
| 623 | // because the real unmap will be invoked when the last | ||
| 624 | // reference to the mapped frame disappears. | ||
| 625 | if (!src->buf[0]) { | ||
| 626 | av_log(src_frames, AV_LOG_ERROR, "Invalid mapping " | ||
| 627 | "found when attempting unmap.\n"); | ||
| 628 | return AVERROR(EINVAL); | ||
| 629 | } | ||
| 630 | hwmap = (HWMapDescriptor*)src->buf[0]->data; | ||
| 631 | av_frame_unref(dst); | ||
| 632 | return av_frame_ref(dst, hwmap->source); | ||
| 633 | } | ||
| 634 | } | ||
| 635 | |||
| 636 | if (src->hw_frames_ctx) { | ||
| 637 | src_frames = (AVHWFramesContext*)src->hw_frames_ctx->data; | ||
| 638 | |||
| 639 | if (src_frames->format == src->format && | ||
| 640 | src_frames->internal->hw_type->map_from) { | ||
| 641 | ret = src_frames->internal->hw_type->map_from(src_frames, | ||
| 642 | dst, src, flags); | ||
| 643 | if (ret != AVERROR(ENOSYS)) | ||
| 644 | return ret; | ||
| 645 | } | ||
| 646 | } | ||
| 647 | |||
| 648 | if (dst->hw_frames_ctx) { | ||
| 649 | dst_frames = (AVHWFramesContext*)dst->hw_frames_ctx->data; | ||
| 650 | |||
| 651 | if (dst_frames->format == dst->format && | ||
| 652 | dst_frames->internal->hw_type->map_to) { | ||
| 653 | ret = dst_frames->internal->hw_type->map_to(dst_frames, | ||
| 654 | dst, src, flags); | ||
| 655 | if (ret != AVERROR(ENOSYS)) | ||
| 656 | return ret; | ||
| 657 | } | ||
| 658 | } | ||
| 659 | |||
| 660 | return AVERROR(ENOSYS); | ||
| 661 | } | ||
| 662 | |||
| 663 | int av_hwframe_ctx_create_derived(AVBufferRef **derived_frame_ctx, | ||
| 664 | enum AVPixelFormat format, | ||
| 665 | AVBufferRef *derived_device_ctx, | ||
| 666 | AVBufferRef *source_frame_ctx, | ||
| 667 | int flags) | ||
| 668 | { | ||
| 669 | AVBufferRef *dst_ref = NULL; | ||
| 670 | AVHWFramesContext *dst = NULL; | ||
| 671 | AVHWFramesContext *src = (AVHWFramesContext*)source_frame_ctx->data; | ||
| 672 | int ret; | ||
| 673 | |||
| 674 | if (src->internal->source_frames) { | ||
| 675 | AVHWFramesContext *src_src = | ||
| 676 | (AVHWFramesContext*)src->internal->source_frames->data; | ||
| 677 | AVHWDeviceContext *dst_dev = | ||
| 678 | (AVHWDeviceContext*)derived_device_ctx->data; | ||
| 679 | |||
| 680 | if (src_src->device_ctx == dst_dev) { | ||
| 681 | // This is actually an unmapping, so we just return a | ||
| 682 | // reference to the source frame context. | ||
| 683 | *derived_frame_ctx = | ||
| 684 | av_buffer_ref(src->internal->source_frames); | ||
| 685 | if (!*derived_frame_ctx) { | ||
| 686 | ret = AVERROR(ENOMEM); | ||
| 687 | goto fail; | ||
| 688 | } | ||
| 689 | return 0; | ||
| 690 | } | ||
| 691 | } | ||
| 692 | |||
| 693 | dst_ref = av_hwframe_ctx_alloc(derived_device_ctx); | ||
| 694 | if (!dst_ref) { | ||
| 695 | ret = AVERROR(ENOMEM); | ||
| 696 | goto fail; | ||
| 697 | } | ||
| 698 | |||
| 699 | dst = (AVHWFramesContext*)dst_ref->data; | ||
| 700 | |||
| 701 | dst->format = format; | ||
| 702 | dst->sw_format = src->sw_format; | ||
| 703 | dst->width = src->width; | ||
| 704 | dst->height = src->height; | ||
| 705 | |||
| 706 | dst->internal->source_frames = av_buffer_ref(source_frame_ctx); | ||
| 707 | if (!dst->internal->source_frames) { | ||
| 708 | ret = AVERROR(ENOMEM); | ||
| 709 | goto fail; | ||
| 710 | } | ||
| 711 | |||
| 712 | ret = av_hwframe_ctx_init(dst_ref); | ||
| 713 | if (ret) | ||
| 714 | goto fail; | ||
| 715 | |||
| 716 | *derived_frame_ctx = dst_ref; | ||
| 717 | return 0; | ||
| 718 | |||
| 719 | fail: | ||
| 720 | if (dst) | ||
| 721 | av_buffer_unref(&dst->internal->source_frames); | ||
| 722 | av_buffer_unref(&dst_ref); | ||
| 723 | return ret; | ||
| 724 | } | ||
| @@ -434,4 +434,91 @@ AVHWFramesConstraints *av_hwdevice_get_hwframe_constraints(AVBufferRef *ref, · 433 unmodified lines | |||
| 434 | */ | 434 | */ |
| 435 | void av_hwframe_constraints_free(AVHWFramesConstraints **constraints); | 435 | void av_hwframe_constraints_free(AVHWFramesConstraints **constraints); |
| 436 | 436 | ||
| 437 | |||
| 438 | /** | ||
| 439 | * Flags to apply to frame mappings. | ||
| 440 | */ | ||
| 441 | enum { | ||
| 442 | /** | ||
| 443 | * The mapping must be readable. | ||
| 444 | */ | ||
| 445 | AV_HWFRAME_MAP_READ = 1 << 0, | ||
| 446 | /** | ||
| 447 | * The mapping must be writeable. | ||
| 448 | */ | ||
| 449 | AV_HWFRAME_MAP_WRITE = 1 << 1, | ||
| 450 | /** | ||
| 451 | * The mapped frame will be overwritten completely in subsequent | ||
| 452 | * operations, so the current frame data need not be loaded. Any values | ||
| 453 | * which are not overwritten are unspecified. | ||
| 454 | */ | ||
| 455 | AV_HWFRAME_MAP_OVERWRITE = 1 << 2, | ||
| 456 | /** | ||
| 457 | * The mapping must be direct. That is, there must not be any copying in | ||
| 458 | * the map or unmap steps. Note that performance of direct mappings may | ||
| 459 | * be much lower than normal memory. | ||
| 460 | */ | ||
| 461 | AV_HWFRAME_MAP_DIRECT = 1 << 3, | ||
| 462 | }; | ||
| 463 | |||
| 464 | /** | ||
| 465 | * Map a hardware frame. | ||
| 466 | * | ||
| 467 | * This has a number of different possible effects, depending on the format | ||
| 468 | * and origin of the src and dst frames. On input, src should be a usable | ||
| 469 | * frame with valid buffers and dst should be blank (typically as just created | ||
| 470 | * by av_frame_alloc()). src should have an associated hwframe context, and | ||
| 471 | * dst may optionally have a format and associated hwframe context. | ||
| 472 | * | ||
| 473 | * If src was created by mapping a frame from the hwframe context of dst, | ||
| 474 | * then this function undoes the mapping - dst is replaced by a reference to | ||
| 475 | * the frame that src was originally mapped from. | ||
| 476 | * | ||
| 477 | * If both src and dst have an associated hwframe context, then this function | ||
| 478 | * attempts to map the src frame from its hardware context to that of dst and | ||
| 479 | * then fill dst with appropriate data to be usable there. This will only be | ||
| 480 | * possible if the hwframe contexts and associated devices are compatible - | ||
| 481 | * given compatible devices, av_hwframe_ctx_create_derived() can be used to | ||
| 482 | * create a hwframe context for dst in which mapping should be possible. | ||
| 483 | * | ||
| 484 | * If src has a hwframe context but dst does not, then the src frame is | ||
| 485 | * mapped to normal memory and should thereafter be usable as a normal frame. | ||
| 486 | * If the format is set on dst, then the mapping will attempt to create dst | ||
| 487 | * with that format and fail if it is not possible. If format is unset (is | ||
| 488 | * AV_PIX_FMT_NONE) then dst will be mapped with whatever the most appropriate | ||
| 489 | * format to use is (probably the sw_format of the src hwframe context). | ||
| 490 | * | ||
| 491 | * A return value of AVERROR(ENOSYS) indicates that the mapping is not | ||
| 492 | * possible with the given arguments and hwframe setup, while other return | ||
| 493 | * values indicate that it failed somehow. | ||
| 494 | * | ||
| 495 | * @param dst Destination frame, to contain the mapping. | ||
| 496 | * @param src Source frame, to be mapped. | ||
| 497 | * @param flags Some combination of AV_HWFRAME_MAP_* flags. | ||
| 498 | * @return Zero on success, negative AVERROR code on failure. | ||
| 499 | */ | ||
| 500 | int av_hwframe_map(AVFrame *dst, const AVFrame *src, int flags); | ||
| 501 | |||
| 502 | |||
| 503 | /** | ||
| 504 | * Create and initialise an AVHWFramesContext as a mapping of another existing | ||
| 505 | * AVHWFramesContext on a different device. | ||
| 506 | * | ||
| 507 | * av_hwframe_ctx_init() should not be called after this. | ||
| 508 | * | ||
| 509 | * @param derived_frame_ctx On success, a reference to the newly created | ||
| 510 | * AVHWFramesContext. | ||
| 511 | * @param derived_device_ctx A reference to the device to create the new | ||
| 512 | * AVHWFramesContext on. | ||
| 513 | * @param source_frame_ctx A reference to an existing AVHWFramesContext | ||
| 514 | * which will be mapped to the derived context. | ||
| 515 | * @param flags Currently unused; should be set to zero. | ||
| 516 | * @return Zero on success, negative AVERROR code on failure. | ||
| 517 | */ | ||
| 518 | int av_hwframe_ctx_create_derived(AVBufferRef **derived_frame_ctx, | ||
| 519 | enum AVPixelFormat format, | ||
| 520 | AVBufferRef *derived_device_ctx, | ||
| 521 | AVBufferRef *source_frame_ctx, | ||
| 522 | int flags); | ||
| 523 | |||
| 437 | #endif /* AVUTIL_HWCONTEXT_H */ | 524 | #endif /* AVUTIL_HWCONTEXT_H */ |
| @@ -85,6 +85,11 @@ typedef struct HWContextType { · 84 unmodified lines | |||
| 85 | const AVFrame *src); | 85 | const AVFrame *src); |
| 86 | int (*transfer_data_from)(AVHWFramesContext *ctx, AVFrame *dst, | 86 | int (*transfer_data_from)(AVHWFramesContext *ctx, AVFrame *dst, |
| 87 | const AVFrame *src); | 87 | const AVFrame *src); |
| 88 | |||
| 89 | int (*map_to)(AVHWFramesContext *ctx, AVFrame *dst, | ||
| 90 | const AVFrame *src, int flags); | ||
| 91 | int (*map_from)(AVHWFramesContext *ctx, AVFrame *dst, | ||
| 92 | const AVFrame *src, int flags); | ||
| 88 | } HWContextType; | 93 | } HWContextType; |
| 89 | 94 | ||
| 90 | struct AVHWDeviceInternal { | 95 | struct AVHWDeviceInternal { |
| @@ -97,8 +102,43 @@ struct AVHWFramesInternal { · 6 unmodified lines | |||
| 97 | void *priv; | 102 | void *priv; |
| 98 | 103 | ||
| 99 | AVBufferPool *pool_internal; | 104 | AVBufferPool *pool_internal; |
| 105 | |||
| 106 | /** | ||
| 107 | * For a derived context, a reference to the original frames | ||
| 108 | * context it was derived from. | ||
| 109 | */ | ||
| 110 | AVBufferRef *source_frames; | ||
| 100 | }; | 111 | }; |
| 101 | 112 | ||
| 113 | typedef struct HWMapDescriptor { | ||
| 114 | /** | ||
| 115 | * A reference to the original source of the mapping. | ||
| 116 | */ | ||
| 117 | AVFrame *source; | ||
| 118 | /** | ||
| 119 | * A reference to the hardware frames context in which this | ||
| 120 | * mapping was made. May be the same as source->hw_frames_ctx, | ||
| 121 | * but need not be. | ||
| 122 | */ | ||
| 123 | AVBufferRef *hw_frames_ctx; | ||
| 124 | /** | ||
| 125 | * Unmap function. | ||
| 126 | */ | ||
| 127 | void (*unmap)(AVHWFramesContext *ctx, | ||
| 128 | struct HWMapDescriptor *hwmap); | ||
| 129 | /** | ||
| 130 | * Hardware-specific private data associated with the mapping. | ||
| 131 | */ | ||
| 132 | void *priv; | ||
| 133 | } HWMapDescriptor; | ||
| 134 | |||
| 135 | int ff_hwframe_map_create(AVBufferRef *hwframe_ref, | ||
| 136 | AVFrame *dst, const AVFrame *src, | ||
| 137 | void (*unmap)(AVHWFramesContext *ctx, | ||
| 138 | HWMapDescriptor *hwmap), | ||
| 139 | void *priv); | ||
| 140 | |||
| 141 | |||
| 102 | extern const HWContextType ff_hwcontext_type_cuda; | 142 | extern const HWContextType ff_hwcontext_type_cuda; |
| 103 | extern const HWContextType ff_hwcontext_type_dxva2; | 143 | extern const HWContextType ff_hwcontext_type_dxva2; |
| 104 | extern const HWContextType ff_hwcontext_type_qsv; | 144 | extern const HWContextType ff_hwcontext_type_qsv; |
| @@ -54,7 +54,7 @@ · 53 unmodified lines | |||
| 54 | */ | 54 | */ |
| 55 | 55 | ||
| 56 | #define LIBAVUTIL_VERSION_MAJOR 55 | 56 | #define LIBAVUTIL_VERSION_MAJOR 55 |
| 57 | #define LIBAVUTIL_VERSION_MINOR 26 | 57 | #define LIBAVUTIL_VERSION_MINOR 27 |
| 58 | #define LIBAVUTIL_VERSION_MICRO 0 | 58 | #define LIBAVUTIL_VERSION_MICRO 0 |
| 59 | 59 | ||
| 60 | #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ | 60 | #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ |