diff --git a/libavcodec/crystalhd.c b/libavcodec/crystalhd.c index 630d02b6f55c44a83619f9a087f9cd102a111b10..b1e5c64829cbe64292af818ebf81664f31055a19 100644 --- a/libavcodec/crystalhd.c +++ b/libavcodec/crystalhd.c @@ -55,6 +55,7 @@ #include <libcrystalhd/libcrystalhd_if.h> #include "avcodec.h" +#include "decode.h" #include "internal.h" #include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" @@ -763,8 +764,7 @@ static int crystalhd_decode_packet(AVCodecContext *avctx, const AVPacket *avpkt) av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: decode_packet\n"); if (avpkt && avpkt->size) { - int32_t tx_free = (int32_t)DtsTxFreeSize(dev); - + uint64_t pts; if (!priv->bframe_bug && (avpkt->size == 6 || avpkt->size == 7)) { /* * Drop frames trigger the bug @@ -809,39 +809,33 @@ static int crystalhd_decode_packet(AVCodecContext *avctx, const AVPacket *avpkt) av_packet_unref(&filter_packet); } - if (avpkt->size < tx_free) { - /* - * Despite being notionally opaque, either libcrystalhd or - * the hardware itself will mangle pts values that are too - * small or too large. The docs claim it should be in units - * of 100ns. Given that we're nominally dealing with a black - * box on both sides, any transform we do has no guarantee of - * avoiding mangling so we need to build a mapping to values - * we know will not be mangled. - */ - uint64_t pts = opaque_list_push(priv, avpkt->pts); - if (!pts) { - ret = AVERROR(ENOMEM); - goto exit; - } - av_log(priv->avctx, AV_LOG_VERBOSE, - "input \"pts\": %"PRIu64"\n", pts); - bc_ret = DtsProcInput(dev, avpkt->data, avpkt->size, pts, 0); - if (bc_ret == BC_STS_BUSY) { - av_log(avctx, AV_LOG_WARNING, - "CrystalHD: ProcInput returned busy\n"); - ret = AVERROR(EAGAIN); - goto exit; - } else if (bc_ret != BC_STS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, - "CrystalHD: ProcInput failed: %u\n", ret); - ret = -1; - goto exit; - } - } else { - av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Input buffer full\n"); + /* + * Despite being notionally opaque, either libcrystalhd or + * the hardware itself will mangle pts values that are too + * small or too large. The docs claim it should be in units + * of 100ns. Given that we're nominally dealing with a black + * box on both sides, any transform we do has no guarantee of + * avoiding mangling so we need to build a mapping to values + * we know will not be mangled. + */ + pts = opaque_list_push(priv, avpkt->pts); + if (!pts) { + ret = AVERROR(ENOMEM); + goto exit; + } + av_log(priv->avctx, AV_LOG_VERBOSE, + "input \"pts\": %"PRIu64"\n", pts); + bc_ret = DtsProcInput(dev, avpkt->data, avpkt->size, pts, 0); + if (bc_ret == BC_STS_BUSY) { + av_log(avctx, AV_LOG_WARNING, + "CrystalHD: ProcInput returned busy\n"); ret = AVERROR(EAGAIN); goto exit; + } else if (bc_ret != BC_STS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, + "CrystalHD: ProcInput failed: %u\n", ret); + ret = -1; + goto exit; } } else { av_log(avctx, AV_LOG_INFO, "CrystalHD: No more input data\n"); @@ -862,9 +856,35 @@ static int crystalhd_receive_frame(AVCodecContext *avctx, AVFrame *frame) CHDContext *priv = avctx->priv_data; HANDLE dev = priv->dev; int got_frame = 0; + int ret = 0; + AVPacket pkt = {0}; av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: receive_frame\n"); + ret = ff_decode_get_packet(avctx, &pkt); + if (ret < 0 && ret != AVERROR_EOF) { + return ret; + } + + while (pkt.size > DtsTxFreeSize(dev)) { + /* + * Block until there is space in the buffer for the next packet. + * We assume that the hardware will make forward progress at this + * point, although in pathological cases that may not happen. + */ + av_log(avctx, AV_LOG_TRACE, "CrystalHD: Waiting for space in input buffer\n"); + } + + ret = crystalhd_decode_packet(avctx, &pkt); + av_packet_unref(&pkt); + // crystalhd_is_buffer_full() should avoid this. + if (ret == AVERROR(EAGAIN)) { + ret = AVERROR_EXTERNAL; + } + if (ret < 0 && ret != AVERROR_EOF) { + return ret; + } + do { bc_ret = DtsGetDriverStatus(dev, &decoder_status); if (bc_ret != BC_STS_SUCCESS) { @@ -873,7 +893,7 @@ static int crystalhd_receive_frame(AVCodecContext *avctx, AVFrame *frame) } if (decoder_status.ReadyListCount == 0) { - av_log(avctx, AV_LOG_INFO, "CrystalHD: Insufficient frames ready. Returning\n"); + av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Insufficient frames ready. Returning\n"); got_frame = 0; rec_ret = RET_OK; break; @@ -907,7 +927,6 @@ static int crystalhd_receive_frame(AVCodecContext *avctx, AVFrame *frame) .priv_class = &x##_crystalhd_class, \ .init = init, \ .close = uninit, \ - .send_packet = crystalhd_decode_packet, \ .receive_frame = crystalhd_receive_frame, \ .flush = flush, \ .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \