mirror of
https://gitee.com/openharmony/third_party_ffmpeg
synced 2024-11-23 19:30:05 +00:00
avcodec/crystalhd: Adapt to new new decode API
The new new decode API requires the decoder to ask for the next input packet, and it cannot just return EAGAIN if that packet cannot be processed yet. This means we must finally confront how we get this decoder to block when the input buffer is full and no output frames are ready yet. In the end, that isn't too hard to achieve - the main trick seems to be that you have to aggressively poll the hardware - it doesn't seem to make any forward progress if you sleep. Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
parent
bddb2343b6
commit
3148387086
@ -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, \
|
||||
|
Loading…
Reference in New Issue
Block a user