Fixes AMF_INPUT_FULL errors with pre-analysis (PA) enabled. Added wait and poll encoder output to free up internal buffers before submitting new frames. Improves stability and performance by accounting for encoder and analysis stage queue limits (incl. lookahead buffering).
Unified · Split
| @@ -204,6 +204,16 @@ int av_cold ff_amf_encode_close(AVCodecContext *avctx) · 203 unmodified lines | ||
| 204 | 204 | av_buffer_unref(&ctx->device_ctx_ref); |
| 205 | 205 | av_fifo_freep2(&ctx->timestamp_list); |
| 206 | 206 | |
| 207 | + if (ctx->output_list) { | |
| 208 | + // release remaining AMF output buffers | |
| 209 | + while(av_fifo_can_read(ctx->output_list)) { | |
| 210 | + AMFBuffer* buffer = NULL; | |
| 211 | + av_fifo_read(ctx->output_list, &buffer, 1); | |
| 212 | + if(buffer != NULL) | |
| 213 | + buffer->pVtbl->Release(buffer); | |
| 214 | + } | |
| 215 | + av_fifo_freep2(&ctx->output_list); | |
| 216 | + } | |
| 207 | 217 | av_freep(&ctx->pts_property_name); |
| 208 | 218 | av_freep(&ctx->av_frame_property_name); |
| 209 | 219 | |
| @@ -306,6 +316,10 @@ int ff_amf_encode_init(AVCodecContext *avctx) · 96 unmodified lines | ||
| 306 | 316 | if (!ctx->timestamp_list) { |
| 307 | 317 | return AVERROR(ENOMEM); |
| 308 | 318 | } |
| 319 | + ctx->output_list = av_fifo_alloc2(2, sizeof(AMFBuffer*), AV_FIFO_FLAG_AUTO_GROW); | |
| 320 | + if (!ctx->output_list) | |
| 321 | + return AVERROR(ENOMEM); | |
| 322 | + | |
| 309 | 323 | ctx->dts_delay = 0; |
| 310 | 324 | |
| 311 | 325 | ctx->hwsurfaces_in_queue = 0; |
| @@ -639,6 +653,22 @@ static int amf_submit_frame_locked(AVCodecContext *avctx, AVFrame *frame, AMFSur · 327 unmodified lines | ||
| 639 | 653 | amf_unlock_context(avctx); |
| 640 | 654 | return ret; |
| 641 | 655 | } |
| 656 | +static AMF_RESULT amf_query_output(AVCodecContext *avctx, AMFBuffer **buffer) | |
| 657 | +{ | |
| 658 | + AMFEncoderContext *ctx = avctx->priv_data; | |
| 659 | + AMFData *data = NULL; | |
| 660 | + AMF_RESULT ret = ctx->encoder->pVtbl->QueryOutput(ctx->encoder, &data); | |
| 661 | + *buffer = NULL; | |
| 662 | + if (data) { | |
| 663 | + AMFGuid guid = IID_AMFBuffer(); | |
| 664 | + data->pVtbl->QueryInterface(data, &guid, (void**)buffer); // query for buffer interface | |
| 665 | + data->pVtbl->Release(data); | |
| 666 | + if (amf_release_attached_frame_ref(ctx, *buffer) == AMF_OK) | |
| 667 | + ctx->hwsurfaces_in_queue--; | |
| 668 | + ctx->encoded_frame++; | |
| 669 | + } | |
| 670 | + return ret; | |
| 671 | +} | |
| 642 | 672 | |
| 643 | 673 | int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) |
| 644 | 674 | { |
| @@ -649,7 +679,7 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) · 4 unmodified lines | ||
| 649 | 679 | AMF_RESULT res; |
| 650 | 680 | int ret; |
| 651 | 681 | AMF_RESULT res_query; |
| 652 | − AMFData *data = NULL; | |
| 682 | + AMFBuffer* buffer = NULL; | |
| 653 | 683 | AVFrame *frame = av_frame_alloc(); |
| 654 | 684 | int block_and_wait; |
| 655 | 685 | int64_t pts = 0; |
| @@ -659,6 +689,14 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) · 3 unmodified lines | ||
| 659 | 689 | av_frame_free(&frame); |
| 660 | 690 | return AVERROR(EINVAL); |
| 661 | 691 | } |
| 692 | + // check if some outputs are available | |
| 693 | + av_fifo_read(ctx->output_list, &buffer, 1); | |
| 694 | + if (buffer != NULL) { // return already retrieved output | |
| 695 | + ret = amf_copy_buffer(avctx, avpkt, buffer); | |
| 696 | + buffer->pVtbl->Release(buffer); | |
| 697 | + return ret; | |
| 698 | + } | |
| 699 | + | |
| 662 | 700 | ret = ff_encode_get_frame(avctx, frame); |
| 663 | 701 | if(ret < 0){ |
| 664 | 702 | if(ret != AVERROR_EOF){ |
| @@ -698,20 +736,10 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) · 33 unmodified lines | ||
| 698 | 736 | do { |
| 699 | 737 | block_and_wait = 0; |
| 700 | 738 | // poll data |
| 701 | − | |
| 702 | − res_query = ctx->encoder->pVtbl->QueryOutput(ctx->encoder, &data); | |
| 703 | − if (data) { | |
| 704 | − // copy data to packet | |
| 705 | − AMFBuffer *buffer; | |
| 706 | − AMFGuid guid = IID_AMFBuffer(); | |
| 707 | − data->pVtbl->QueryInterface(data, &guid, (void**)&buffer); // query for buffer interface | |
| 739 | + res_query = amf_query_output(avctx, &buffer); | |
| 740 | + if (buffer) { | |
| 708 | 741 | ret = amf_copy_buffer(avctx, avpkt, buffer); |
| 709 | − if (amf_release_attached_frame_ref(ctx, buffer) == AMF_OK) { | |
| 710 | − ctx->hwsurfaces_in_queue--; | |
| 711 | − } | |
| 712 | − ctx->encoded_frame++; | |
| 713 | 742 | buffer->pVtbl->Release(buffer); |
| 714 | − data->pVtbl->Release(data); | |
| 715 | 743 | |
| 716 | 744 | AMF_RETURN_IF_FALSE(ctx, ret >= 0, ret, "amf_copy_buffer() failed with error %d\n", ret); |
| 717 | 745 | |
| @@ -737,12 +765,29 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) · 19 unmodified lines | ||
| 737 | 765 | |
| 738 | 766 | if (res_query == AMF_EOF) { |
| 739 | 767 | ret = AVERROR_EOF; |
| 740 | − } else if (data == NULL) { | |
| 768 | + } else if (buffer == NULL) { | |
| 741 | 769 | ret = AVERROR(EAGAIN); |
| 742 | 770 | } else { |
| 743 | 771 | if(surface) { |
| 744 | 772 | // resubmit surface |
| 745 | − res = ctx->encoder->pVtbl->SubmitInput(ctx->encoder, (AMFData*)surface); | |
| 773 | + do { | |
| 774 | + res = ctx->encoder->pVtbl->SubmitInput(ctx->encoder, (AMFData*)surface); | |
| 775 | + if (res != AMF_INPUT_FULL) | |
| 776 | + break; | |
| 777 | + | |
| 778 | + if (!ctx->query_timeout_supported) | |
| 779 | + av_usleep(1000); | |
| 780 | + | |
| 781 | + // Need to free up space in the encoder queue. | |
| 782 | + // The number of retrieved outputs is limited currently to 21 | |
| 783 | + amf_query_output(avctx, &buffer); | |
| 784 | + if (buffer != NULL) { | |
| 785 | + ret = av_fifo_write(ctx->output_list, &buffer, 1); | |
| 786 | + if (ret < 0) | |
| 787 | + return ret; | |
| 788 | + } | |
| 789 | + } while(res == AMF_INPUT_FULL); | |
| 790 | + | |
| 746 | 791 | surface->pVtbl->Release(surface); |
| 747 | 792 | if (res == AMF_INPUT_FULL) { |
| 748 | 793 | av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed SubmitInput returned AMF_INPUT_FULL- should not happen\n"); |
| @@ -61,6 +61,7 @@ typedef struct AMFEncoderContext { · 60 unmodified lines | ||
| 61 | 61 | int64_t dts_delay; |
| 62 | 62 | int64_t submitted_frame; |
| 63 | 63 | int64_t encoded_frame; |
| 64 | + AVFifo *output_list; | |
| 64 | 65 | |
| 65 | 66 | // common encoder options |
| 66 | 67 | |