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 | 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 | 20 | 2016-xx-xx - xxxxxxx - lavu 55.25.0 - pixfmt.h |
| 17 | 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 | 158 | AVHWFramesContext *ctx = (AVHWFramesContext*)data; |
| 159 | 159 | |
| 160 | − if (ctx->internal->pool_internal) | |
| 161 | − av_buffer_pool_uninit(&ctx->internal->pool_internal); | |
| 160 | + if (ctx->internal->source_frames) { | |
| 161 | + av_buffer_unref(&ctx->internal->source_frames); | |
| 162 | 162 | |
| 163 | − if (ctx->internal->hw_type->frames_uninit) | |
| 164 | − ctx->internal->hw_type->frames_uninit(ctx); | |
| 163 | + } else { | |
| 164 | + if (ctx->internal->pool_internal) | |
| 165 | + av_buffer_pool_uninit(&ctx->internal->pool_internal); | |
| 165 | 166 | |
| 166 | − if (ctx->free) | |
| 167 | − ctx->free(ctx); | |
| 167 | + if (ctx->internal->hw_type->frames_uninit) | |
| 168 | + ctx->internal->hw_type->frames_uninit(ctx); | |
| 169 | + | |
| 170 | + if (ctx->free) | |
| 171 | + ctx->free(ctx); | |
| 172 | + } | |
| 168 | 173 | |
| 169 | 174 | av_buffer_unref(&ctx->device_ref); |
| 170 | 175 | |
| @@ -266,6 +271,11 @@ int av_hwframe_ctx_init(AVBufferRef *ref) · 95 unmodified lines | ||
| 266 | 271 | const enum AVPixelFormat *pix_fmt; |
| 267 | 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 | 279 | /* validate the pixel format */ |
| 270 | 280 | for (pix_fmt = ctx->internal->hw_type->pix_fmts; *pix_fmt != AV_PIX_FMT_NONE; pix_fmt++) { |
| 271 | 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 | 406 | AVHWFramesContext *ctx = (AVHWFramesContext*)hwframe_ref->data; |
| 397 | 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 | 438 | if (!ctx->internal->hw_type->frames_get_buffer) |
| 400 | 439 | return AVERROR(ENOSYS); |
| 401 | 440 | |
| @@ -495,3 +534,191 @@ fail: · 93 unmodified lines | ||
| 495 | 534 | *pdevice_ref = NULL; |
| 496 | 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 | 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 | 524 | #endif /* AVUTIL_HWCONTEXT_H */ |
| @@ -85,6 +85,11 @@ typedef struct HWContextType { · 84 unmodified lines | ||
| 85 | 85 | const AVFrame *src); |
| 86 | 86 | int (*transfer_data_from)(AVHWFramesContext *ctx, AVFrame *dst, |
| 87 | 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 | 93 | } HWContextType; |
| 89 | 94 | |
| 90 | 95 | struct AVHWDeviceInternal { |
| @@ -97,8 +102,43 @@ struct AVHWFramesInternal { · 6 unmodified lines | ||
| 97 | 102 | void *priv; |
| 98 | 103 | |
| 99 | 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 | 142 | extern const HWContextType ff_hwcontext_type_cuda; |
| 103 | 143 | extern const HWContextType ff_hwcontext_type_dxva2; |
| 104 | 144 | extern const HWContextType ff_hwcontext_type_qsv; |
| @@ -54,7 +54,7 @@ · 53 unmodified lines | ||
| 54 | 54 | */ |
| 55 | 55 | |
| 56 | 56 | #define LIBAVUTIL_VERSION_MAJOR 55 |
| 57 | −#define LIBAVUTIL_VERSION_MINOR 26 | |
| 57 | +#define LIBAVUTIL_VERSION_MINOR 27 | |
| 58 | 58 | #define LIBAVUTIL_VERSION_MICRO 0 |
| 59 | 59 | |
| 60 | 60 | #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ |