| @@ -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 | +} |