直接看代码:
int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
{
AVCodecInternal *avci = avctx->internal;
int ret;
if (!avcodec_is_open(avctx) || !av_codec_is_decoder(avctx->codec))
return AVERROR(EINVAL);
if (avctx->internal->draining)
return AVERROR_EOF;
if (avpkt && !avpkt->size && avpkt->data)
return AVERROR(EINVAL);
av_packet_unref(avci->buffer_pkt);
if (avpkt && (avpkt->data || avpkt->side_data_elems)) {
//这里要注意点,数据的交换喔,把avpkt智能指针一样引用到avci->buffer_pkt
ret = av_packet_ref(avci->buffer_pkt, avpkt);
if (ret < 0)
return ret;
}
//看这里,把数据再交换到filter中,什么是fileter?
ret = av_bsf_send_packet(avci->filter.bsfs[0], avci->buffer_pkt);
if (ret < 0) {
av_packet_unref(avci->buffer_pkt);
return ret;
}
if (!avci->buffer_frame->buf[0]) {
//接着在这里,注意这里buffer_frame AVFrame *buffer_frame;是个指针
//解析结束后将数据拷贝到avci->buffer_frame
ret = decode_receive_frame_internal(avctx, avci->buffer_frame);
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
return ret;
}
return 0;
}
/**
* Submit a packet for filtering.
*
* After sending each packet, the filter must be completely drained by calling
* av_bsf_receive_packet() repeatedly until it returns AVERROR(EAGAIN) or
* AVERROR_EOF.
*
* @param pkt the packet to filter. The bitstream filter will take ownership of
* the packet and reset the contents of pkt. pkt is not touched if an error occurs.
* This parameter may be NULL, which signals the end of the stream (i.e. no more
* packets will be sent). That will cause the filter to output any packets it
* may have buffered internally.
*
* @return 0 on success, a negative AVERROR on error.
*/
int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt)
{
int ret;
if (!pkt || (!pkt->data && !pkt->side_data_elems)) {
ctx->internal->eof = 1;
return 0;
}
if (ctx->internal->eof) {
av_log(ctx, AV_LOG_ERROR, "A non-NULL packet sent after an EOF.\n");
return AVERROR(EINVAL);
}
if (ctx->internal->buffer_pkt->data ||
ctx->internal->buffer_pkt->side_data_elems)
return AVERROR(EAGAIN);
//看这里1Ensure the data described by a given packet is reference counted.
ret = av_packet_make_refcounted(pkt);
if (ret < 0)
return ret;
//接着这里2
av_packet_move_ref(ctx->internal->buffer_pkt, pkt);
return 0;
}
//就是把pktdata从data转到buf中
int av_packet_make_refcounted(AVPacket *pkt)
{
int ret;
if (pkt->buf)
return 0;
ret = packet_alloc(&pkt->buf, pkt->size);
if (ret < 0)
return ret;
av_assert1(!pkt->size || pkt->data);
if (pkt->size)
memcpy(pkt->buf->data, pkt->data, pkt->size);
pkt->data = pkt->buf->data;
return 0;
}
//ctx->internal->buffer_pkt数据换个位置 pkt
void av_packet_move_ref(AVPacket *dst, AVPacket *src)
{
*dst = *src;
av_init_packet(src);
src->data = NULL;
src->size = 0;
}
static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
{
AVCodecInternal *avci = avctx->internal;
int ret;
av_assert0(!frame->buf[0]);
if (avctx->codec->receive_frame)
/**
* Decode API with decoupled packet/frame dataflow. This function is called
* to get one output frame. It should call ff_decode_get_packet() to obtain
* input data.
*/
//这里执行的实际函数在这里是cuvid_output_frame(我是采用cuda解码)
//libavcodec中cuviddec.c
ret = avctx->codec->receive_frame(avctx, frame);
else
ret = decode_simple_receive_frame(avctx, frame);
if (ret == AVERROR_EOF)
avci->draining_done = 1;
if (!ret) {
/* the only case where decode data is not set should be decoders
* that do not call ff_get_buffer() */
av_assert0((frame->private_ref && frame->private_ref->size == sizeof(FrameDecodeData)) ||
!(avctx->codec->capabilities & AV_CODEC_CAP_DR1));
if (frame->private_ref) {
FrameDecodeData *fdd = (FrameDecodeData*)frame->private_ref->data;
if (fdd->post_process) {
ret = fdd->post_process(avctx, frame);
if (ret < 0) {
av_frame_unref(frame);
return ret;
}
}
}
}
/* free the per-frame decode data */
av_buffer_unref(&frame->private_ref);
return ret;
}
具体看这里
AVCodec ff_##x##_cuvid_decoder = { \
.name = #x "_cuvid", \
.long_name = NULL_IF_CONFIG_SMALL("Nvidia CUVID " #X " decoder"), \
.type = AVMEDIA_TYPE_VIDEO, \
.id = AV_CODEC_ID_##X, \
.priv_data_size = sizeof(CuvidContext), \
.priv_class = &x##_cuvid_class, \
.init = cuvid_decode_init, \
.close = cuvid_decode_end, \
.decode = cuvid_decode_frame, \
.receive_frame = cuvid_output_frame, \
.flush = cuvid_flush, \
.bsfs = bsf_name, \
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \
.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, \
AV_PIX_FMT_NV12, \
AV_PIX_FMT_P010, \
AV_PIX_FMT_P016, \
AV_PIX_FMT_NONE }, \
.hw_configs = cuvid_hw_configs, \
.wrapper_name = "cuvid", \
};
static int cuvid_output_frame(AVCodecContext *avctx, AVFrame *frame)
{
CuvidContext *ctx = avctx->priv_data;
AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)ctx->hwdevice->data;
AVCUDADeviceContext *device_hwctx = device_ctx->hwctx;
CUcontext dummy, cuda_ctx = device_hwctx->cuda_ctx;
CUdeviceptr mapped_frame = 0;
int ret = 0, eret = 0;
av_log(avctx, AV_LOG_TRACE, "cuvid_output_frame\n");
if (ctx->decoder_flushing) {
ret = cuvid_decode_packet(avctx, NULL);
if (ret < 0 && ret != AVERROR_EOF)
return ret;
}
//检查是否是满的
if (!cuvid_is_buffer_full(avctx)) {
AVPacket pkt = {0};
//获取packet
/**
* Called by decoders to get the next packet for decoding.
*
* @param pkt An empty packet to be filled with data.
* @return 0 if a new reference has been successfully written to pkt
* AVERROR(EAGAIN) if no data is currently available
* AVERROR_EOF if and end of stream has been reached, so no more data
* will be available
*/
ret = ff_decode_get_packet(avctx, &pkt);
if (ret < 0 && ret != AVERROR_EOF)
return ret;
//在这里解码
ret = cuvid_decode_packet(avctx, &pkt);
av_packet_unref(&pkt);
// cuvid_is_buffer_full() should avoid this.
if (ret == AVERROR(EAGAIN))
ret = AVERROR_EXTERNAL;
if (ret < 0 && ret != AVERROR_EOF)
return ret;
}
ret = CHECK_CU(ctx->cudl->cuCtxPushCurrent(cuda_ctx));
if (ret < 0)
return ret;
//解码器有一个fifo队列
if (av_fifo_size(ctx->frame_queue)) {
const AVPixFmtDescriptor *pixdesc;
CuvidParsedFrame parsed_frame;
CUVIDPROCPARAMS params;
unsigned int pitch = 0;
int offset = 0;
int i;
//这里执行parse_frame
av_fifo_generic_read(ctx->frame_queue, &parsed_frame, sizeof(CuvidParsedFrame), NULL);
memset(¶ms, 0, sizeof(params));
params.progressive_frame = parsed_frame.dispinfo.progressive_frame;
params.second_field = parsed_frame.second_field;
params.top_field_first = parsed_frame.dispinfo.top_field_first;
/*
使用nvidia进行硬件解码需要了解一下device内存(可以叫显存或设备内存)和系统内存的数据处理方法。在解码完成后,视频YUV数据是在device内存中的,所以需要使用nvidia提供的接口把数据弄出来。涉及的接口主要有:cuMemAllocHost, cuMemFreeHost, cuvidMapVideoFrame, cuvidUnmapVideoFrame, cuMemcpyDtoHAsync。其中,cuMemAllocHost是用来创建系统及显卡都可访问的系统内存。cuvidMapVideoFrame可以获取到设备内存中指定的YUV数据地址。最后通过cuMemcpyDtoHAsync将设备内存中指定的数据copy到系统内存中。
*/
ret = CHECK_CU(ctx->cvdl->cuvidMapVideoFrame(ctx->cudecoder, parsed_frame.dispinfo.picture_index, &mapped_frame, &pitch, ¶ms));
if (ret < 0)
goto error;
if (avctx->pix_fmt == AV_PIX_FMT_CUDA) {
//将数据拷贝到frame
ret = av_hwframe_get_buffer(ctx->hwframe, frame, 0);
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR, "av_hwframe_get_buffer failed\n");
goto error;
}
//拷贝特性
ret = ff_decode_frame_props(avctx, frame);
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR, "ff_decode_frame_props failed\n");
goto error;
}
pixdesc = av_pix_fmt_desc_get(avctx->sw_pix_fmt);
for (i = 0; i < pixdesc->nb_components; i++) {
int height = avctx->height >> (i ? pixdesc->log2_chroma_h : 0);
CUDA_MEMCPY2D cpy = {
.srcMemoryType = CU_MEMORYTYPE_DEVICE,
.dstMemoryType = CU_MEMORYTYPE_DEVICE,
.srcDevice = mapped_frame,
.dstDevice = (CUdeviceptr)frame->data[i],
.srcPitch = pitch,
.dstPitch = frame->linesize[i],
.srcY = offset,
.WidthInBytes = FFMIN(pitch, frame->linesize[i]),
.Height = height,
};
//这里cpy是异步的
ret = CHECK_CU(ctx->cudl->cuMemcpy2DAsync(&cpy, device_hwctx->stream));
if (ret < 0)
goto error;
offset += height;
}
} else if (avctx->pix_fmt == AV_PIX_FMT_NV12 ||
avctx->pix_fmt == AV_PIX_FMT_P010 ||
avctx->pix_fmt == AV_PIX_FMT_P016 ||
avctx->pix_fmt == AV_PIX_FMT_YUV444P ||
avctx->pix_fmt == AV_PIX_FMT_YUV444P16) {
unsigned int offset = 0;
AVFrame *tmp_frame = av_frame_alloc();
if (!tmp_frame) {
av_log(avctx, AV_LOG_ERROR, "av_frame_alloc failed\n");
ret = AVERROR(ENOMEM);
goto error;
}
pixdesc = av_pix_fmt_desc_get(avctx->sw_pix_fmt);
tmp_frame->format = AV_PIX_FMT_CUDA;
tmp_frame->hw_frames_ctx = av_buffer_ref(ctx->hwframe);
tmp_frame->width = avctx->width;
tmp_frame->height = avctx->height;
/*
* Note that the following logic would not work for three plane
* YUV420 because the pitch value is different for the chroma
* planes.
*/
for (i = 0; i < pixdesc->nb_components; i++) {
tmp_frame->data[i] = (uint8_t*)mapped_frame + offset;
tmp_frame->linesize[i] = pitch;
offset += pitch * (avctx->height >> (i ? pixdesc->log2_chroma_h : 0));
}
//decode.c 1854 主要就是检查avctx长宽是否正确,然后给framdeptsde等等赋值
ret = ff_get_buffer(avctx, frame, 0);
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR, "ff_get_buffer failed\n");
av_frame_free(&tmp_frame);
goto error;
}
//从tmpdao frame
ret = av_hwframe_transfer_data(frame, tmp_frame, 0);
if (ret) {
av_log(avctx, AV_LOG_ERROR, "av_hwframe_transfer_data failed\n");
av_frame_free(&tmp_frame);
goto error;
}
av_frame_free(&tmp_frame);
} else {
ret = AVERROR_BUG;
goto error;
}
frame->key_frame = ctx->key_frame[parsed_frame.dispinfo.picture_index];
frame->width = avctx->width;
frame->height = avctx->height;
if (avctx->pkt_timebase.num && avctx->pkt_timebase.den)
frame->pts = av_rescale_q(parsed_frame.dispinfo.timestamp, (AVRational){1, 10000000}, avctx->pkt_timebase);
else
frame->pts = parsed_frame.dispinfo.timestamp;
if (parsed_frame.second_field) {
if (ctx->prev_pts == INT64_MIN) {
ctx->prev_pts = frame->pts;
frame->pts += (avctx->pkt_timebase.den * avctx->framerate.den) / (avctx->pkt_timebase.num * avctx->framerate.num);
} else {
int pts_diff = (frame->pts - ctx->prev_pts) / 2;
ctx->prev_pts = frame->pts;
frame->pts += pts_diff;
}
}
/* CUVIDs opaque reordering breaks the internal pkt logic.
* So set pkt_pts and clear all the other pkt_ fields.
*/
#if FF_API_PKT_PTS
FF_DISABLE_DEPRECATION_WARNINGS
frame->pkt_pts = frame->pts;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
frame->pkt_pos = -1;
frame->pkt_duration = 0;
frame->pkt_size = -1;
frame->interlaced_frame = !parsed_frame.is_deinterlacing && !parsed_frame.dispinfo.progressive_frame;
if (frame->interlaced_frame)
frame->top_field_first = parsed_frame.dispinfo.top_field_first;
} else if (ctx->decoder_flushing) {
ret = AVERROR_EOF;
} else {
ret = AVERROR(EAGAIN);
}
error:
if (mapped_frame)
eret = CHECK_CU(ctx->cvdl->cuvidUnmapVideoFrame(ctx->cudecoder, mapped_frame));
eret = CHECK_CU(ctx->cudl->cuCtxPopCurrent(&dummy));
if (eret < 0)
return eret;
else
return ret;
}
static int cuvid_is_buffer_full(AVCodecContext *avctx)
{
CuvidContext *ctx = avctx->priv_data;
int delay = ctx->cuparseinfo.ulMaxDisplayDelay;
if (ctx->deint_mode != cudaVideoDeinterlaceMode_Weave && !ctx->drop_second_field)
delay *= 2;
/**
* Return the amount of data in bytes in the AVFifoBuffer, that is the
* amount of data you can read from it.
* @param f AVFifoBuffer to read from
* @return size
*/
//int av_fifo_size(const AVFifoBuffer *f);
return (av_fifo_size(ctx->frame_queue) / sizeof(CuvidParsedFrame)) + delay >= ctx->nb_surfaces;
}
int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt)
{
AVCodecInternal *avci = avctx->internal;
int ret;
if (avci->draining)
return AVERROR_EOF;
//这里执行滤镜
ret = bsfs_poll(avctx, pkt);
if (ret == AVERROR_EOF)
avci->draining = 1;
if (ret < 0)
return ret;
//这里pkt这种的数据交换到avci->last_pkt_props中去
ret = extract_packet_props(avctx->internal, pkt);
if (ret < 0)
goto finish;
ret = apply_param_change(avctx, pkt);
if (ret < 0)
goto finish;
if (avctx->codec->receive_frame)
avci->compat_decode_consumed += pkt->size;
return 0;
finish:
av_packet_unref(pkt);
return ret;
}
/* try to get one output packet from the filter chain */
static int bsfs_poll(AVCodecContext *avctx, AVPacket *pkt)
{
DecodeFilterContext *s = &avctx->internal->filter;
int idx, ret;
/* start with the last filter in the chain */
idx = s->nb_bsfs - 1;
while (idx >= 0) {
/* request a packet from the currently selected filter */
/*
int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt)
{
//过滤器的函数-------------什么是过滤器-------------------
//https://blog.csdn.net/newchenxf/article/details/51364105
return ctx->filter->filter(ctx, pkt);
}
*/
//从过滤器中获取数据,这个从前面确定的过滤器中执行,相当于执行滤镜而已
//h264_mp4toannexb
//对H264数据要做一个处理才能把AVPacket有效的转为CUVIDSOURCEDATAPACKET
ret = av_bsf_receive_packet(s->bsfs[idx], pkt);
if (ret == AVERROR(EAGAIN)) {
/* no packets available, try the next filter up the chain */
idx--;
continue;
} else if (ret < 0 && ret != AVERROR_EOF) {
return ret;
}
/* got a packet or EOF -- pass it to the caller or to the next filter
* down the chain */
if (idx == s->nb_bsfs - 1) {
return ret;
} else {
idx++;
//解码之前的预处理,就是pkt中数据的交换而已
ret = av_bsf_send_packet(s->bsfs[idx], ret < 0 ? NULL : pkt);
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR,
"Error pre-processing a packet before decoding\n");
av_packet_unref(pkt);
return ret;
}
}
}
return AVERROR(EAGAIN);
}
//
static int extract_packet_props(AVCodecInternal *avci, const AVPacket *pkt)
{
int ret = 0;
//解引用
av_packet_unref(avci->last_pkt_props);
if (pkt) {
//将pkt中数据拷贝到last_pkt_props中
ret = av_packet_copy_props(avci->last_pkt_props, pkt);
if (!ret)
avci->last_pkt_props->size = pkt->size; // HACK: Needed for ff_decode_frame_props().
}
return ret;
}
//将数据包中的属性拷贝一次,不包含data
int av_packet_copy_props(AVPacket *dst, const AVPacket *src)
{
int i;
dst->pts = src->pts;
dst->dts = src->dts;
dst->pos = src->pos;
dst->duration = src->duration;
#if FF_API_CONVERGENCE_DURATION
FF_DISABLE_DEPRECATION_WARNINGS
dst->convergence_duration = src->convergence_duration;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
dst->flags = src->flags;
dst->stream_index = src->stream_index;
dst->side_data = NULL;
dst->side_data_elems = 0;
for (i = 0; i < src->side_data_elems; i++) {
enum AVPacketSideDataType type = src->side_data[i].type;
int size = src->side_data[i].size;
uint8_t *src_data = src->side_data[i].data;
uint8_t *dst_data = av_packet_new_side_data(dst, type, size);
if (!dst_data) {
av_packet_free_side_data(dst);
return AVERROR(ENOMEM);
}
memcpy(dst_data, src_data, size);
}
return 0;
}
static int apply_param_change(AVCodecContext *avctx, const AVPacket *avpkt)
{
int size = 0, ret;
const uint8_t *data;
uint32_t flags;
int64_t val;
//获取元数据
data = av_packet_get_side_data(avpkt, AV_PKT_DATA_PARAM_CHANGE, &size);
if (!data)
return 0;
if (!(avctx->codec->capabilities & AV_CODEC_CAP_PARAM_CHANGE)) {
av_log(avctx, AV_LOG_ERROR, "This decoder does not support parameter "
"changes, but PARAM_CHANGE side data was sent to it.\n");
ret = AVERROR(EINVAL);
goto fail2;
}
if (size < 4)
goto fail;
flags = bytestream_get_le32(&data);
size -= 4;
if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT) {
if (size < 4)
goto fail;
val = bytestream_get_le32(&data);
if (val <= 0 || val > INT_MAX) {
av_log(avctx, AV_LOG_ERROR, "Invalid channel count");
ret = AVERROR_INVALIDDATA;
goto fail2;
}
avctx->channels = val;
size -= 4;
}
if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) {
if (size < 8)
goto fail;
avctx->channel_layout = bytestream_get_le64(&data);
size -= 8;
}
if (flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE) {
if (size < 4)
goto fail;
val = bytestream_get_le32(&data);
if (val <= 0 || val > INT_MAX) {
av_log(avctx, AV_LOG_ERROR, "Invalid sample rate");
ret = AVERROR_INVALIDDATA;
goto fail2;
}
avctx->sample_rate = val;
size -= 4;
}
if (flags & AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS) {
if (size < 8)
goto fail;
avctx->width = bytestream_get_le32(&data);
avctx->height = bytestream_get_le32(&data);
size -= 8;
ret = ff_set_dimensions(avctx, avctx->width, avctx->height);
if (ret < 0)
goto fail2;
}
return 0;
fail:
av_log(avctx, AV_LOG_ERROR, "PARAM_CHANGE side data too small.\n");
ret = AVERROR_INVALIDDATA;
fail2:
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR, "Error applying parameter changes.\n");
if (avctx->err_recognition & AV_EF_EXPLODE)
return ret;
}
return 0;
}
uint8_t *av_packet_get_side_data(const AVPacket *pkt, enum AVPacketSideDataType type,
int *size)
{
int i;
for (i = 0; i < pkt->side_data_elems; i++) {
if (pkt->side_data[i].type == type) {
if (size)
*size = pkt->side_data[i].size;
return pkt->side_data[i].data;
}
}
if (size)
*size = 0;
return NULL;
}
static int cuvid_decode_packet(AVCodecContext *avctx, const AVPacket *avpkt)
{
CuvidContext *ctx = avctx->priv_data;
AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)ctx->hwdevice->data;
AVCUDADeviceContext *device_hwctx = device_ctx->hwctx;
CUcontext dummy, cuda_ctx = device_hwctx->cuda_ctx;
//特别注意这个pkt
CUVIDSOURCEDATAPACKET cupkt;
AVPacket filter_packet = { 0 };
AVPacket filtered_packet = { 0 };
int ret = 0, eret = 0, is_flush = ctx->decoder_flushing;
av_log(avctx, AV_LOG_TRACE, "cuvid_decode_packet\n");
if (is_flush && avpkt && avpkt->size)
return AVERROR_EOF;
if (cuvid_is_buffer_full(avctx) && avpkt && avpkt->size)
return AVERROR(EAGAIN);
if (ctx->bsf && avpkt && avpkt->size) {
if ((ret = av_packet_ref(&filter_packet, avpkt)) < 0) {
av_log(avctx, AV_LOG_ERROR, "av_packet_ref failed\n");
return ret;
}
if ((ret = av_bsf_send_packet(ctx->bsf, &filter_packet)) < 0) {
av_log(avctx, AV_LOG_ERROR, "av_bsf_send_packet failed\n");
av_packet_unref(&filter_packet);
return ret;
}
if ((ret = av_bsf_receive_packet(ctx->bsf, &filtered_packet)) < 0) {
av_log(avctx, AV_LOG_ERROR, "av_bsf_receive_packet failed\n");
return ret;
}
avpkt = &filtered_packet;
}
ret = CHECK_CU(ctx->cudl->cuCtxPushCurrent(cuda_ctx));
if (ret < 0) {
av_packet_unref(&filtered_packet);
return ret;
}
memset(&cupkt, 0, sizeof(cupkt));
if (avpkt && avpkt->size) {
//将pkt中的数据拷贝到cupkt中
cupkt.payload_size = avpkt->size;
cupkt.payload = avpkt->data;
if (avpkt->pts != AV_NOPTS_VALUE) {
cupkt.flags = CUVID_PKT_TIMESTAMP;
if (avctx->pkt_timebase.num && avctx->pkt_timebase.den)
cupkt.timestamp = av_rescale_q(avpkt->pts, avctx->pkt_timebase, (AVRational){1, 10000000});
else
cupkt.timestamp = avpkt->pts;
}
} else {
cupkt.flags = CUVID_PKT_ENDOFSTREAM;
ctx->decoder_flushing = 1;
}
//把数据送到gpu中去
ret = CHECK_CU(ctx->cvdl->cuvidParseVideoData(ctx->cuparser, &cupkt));
av_packet_unref(&filtered_packet);
if (ret < 0)
goto error;
// cuvidParseVideoData doesn't return an error just because stuff failed...
if (ctx->internal_error) {
av_log(avctx, AV_LOG_ERROR, "cuvid decode callback error\n");
ret = ctx->internal_error;
goto error;
}
error:
eret = CHECK_CU(ctx->cudl->cuCtxPopCurrent(&dummy));
if (eret < 0)
return eret;
else if (ret < 0)
return ret;
else if (is_flush)
return AVERROR_EOF;
else
return 0;
}