ffmpeg/libavcodec/lcevcdec.c

320 lines
9.0 KiB
C

/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config_components.h"
#include "libavutil/avassert.h"
#include "libavutil/frame.h"
#include "libavutil/imgutils.h"
#include "libavutil/log.h"
#include "libavutil/mem.h"
#include "decode.h"
#include "lcevcdec.h"
#if CONFIG_LIBLCEVC_DEC
static LCEVC_ColorFormat map_format(int format)
{
switch (format) {
case AV_PIX_FMT_YUV420P:
return LCEVC_I420_8;
case AV_PIX_FMT_YUV420P10:
return LCEVC_I420_10_LE;
case AV_PIX_FMT_NV12:
return LCEVC_NV12_8;
case AV_PIX_FMT_NV21:
return LCEVC_NV21_8;
case AV_PIX_FMT_GRAY8:
return LCEVC_GRAY_8;
}
return LCEVC_ColorFormat_Unknown;
}
static int alloc_base_frame(void *logctx, LCEVC_DecoderHandle decoder,
const AVFrame *frame, LCEVC_PictureHandle *picture)
{
LCEVC_PictureDesc desc;
LCEVC_ColorFormat fmt = map_format(frame->format);
LCEVC_PictureLockHandle lock;
uint8_t *data[4] = { NULL };
int linesizes[4] = { 0 };
uint32_t planes;
LCEVC_ReturnCode res;
res = LCEVC_DefaultPictureDesc(&desc, fmt, frame->width, frame->height);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
desc.cropTop = frame->crop_top;
desc.cropBottom = frame->crop_bottom;
desc.cropLeft = frame->crop_left;
desc.cropRight = frame->crop_right;
desc.sampleAspectRatioNum = frame->sample_aspect_ratio.num;
desc.sampleAspectRatioDen = frame->sample_aspect_ratio.den;
/* Allocate LCEVC Picture */
res = LCEVC_AllocPicture(decoder, &desc, picture);
if (res != LCEVC_Success) {
return AVERROR_EXTERNAL;
}
res = LCEVC_LockPicture(decoder, *picture, LCEVC_Access_Write, &lock);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
res = LCEVC_GetPicturePlaneCount(decoder, *picture, &planes);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
for (unsigned i = 0; i < planes; i++) {
LCEVC_PicturePlaneDesc plane;
res = LCEVC_GetPictureLockPlaneDesc(decoder, lock, i, &plane);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
data[i] = plane.firstSample;
linesizes[i] = plane.rowByteStride;
}
av_image_copy2(data, linesizes, frame->data, frame->linesize,
frame->format, frame->width, frame->height);
res = LCEVC_UnlockPicture(decoder, lock);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
return 0;
}
static int alloc_enhanced_frame(void *logctx, LCEVC_DecoderHandle decoder,
const AVFrame *frame, LCEVC_PictureHandle *picture)
{
LCEVC_PictureDesc desc ;
LCEVC_ColorFormat fmt = map_format(frame->format);
LCEVC_PicturePlaneDesc planes[4] = { 0 };
int width = frame->width * 2 / FFMAX(frame->sample_aspect_ratio.den, 1);
int height = frame->height * 2 / FFMAX(frame->sample_aspect_ratio.num, 1);
LCEVC_ReturnCode res;
res = LCEVC_DefaultPictureDesc(&desc, fmt, width, height);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
/* Set plane description */
for (int i = 0; i < 4; i++) {
planes[i].firstSample = frame->data[i];
planes[i].rowByteStride = frame->linesize[i];
}
/* Allocate LCEVC Picture */
res = LCEVC_AllocPictureExternal(decoder, &desc, NULL, planes, picture);
if (res != LCEVC_Success) {
return AVERROR_EXTERNAL;
}
return 0;
}
static int lcevc_send_frame(void *logctx, FFLCEVCContext *lcevc, const AVFrame *in)
{
const AVFrameSideData *sd = av_frame_get_side_data(in, AV_FRAME_DATA_LCEVC);
LCEVC_PictureHandle picture;
LCEVC_ReturnCode res;
int ret = 0;
if (!sd)
return 1;
res = LCEVC_SendDecoderEnhancementData(lcevc->decoder, in->pts, 0, sd->data, sd->size);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
ret = alloc_base_frame(logctx, lcevc->decoder, in, &picture);
if (ret < 0)
return ret;
res = LCEVC_SendDecoderBase(lcevc->decoder, in->pts, 0, picture, -1, NULL);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
memset(&picture, 0, sizeof(picture));
ret = alloc_enhanced_frame(logctx, lcevc->decoder, in, &picture);
if (ret < 0)
return ret;
res = LCEVC_SendDecoderPicture(lcevc->decoder, picture);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
return 0;
}
static int generate_output(void *logctx, FFLCEVCContext *lcevc, AVFrame *out)
{
LCEVC_PictureDesc desc;
LCEVC_DecodeInformation info;
LCEVC_PictureHandle picture;
LCEVC_ReturnCode res;
res = LCEVC_ReceiveDecoderPicture(lcevc->decoder, &picture, &info);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
res = LCEVC_GetPictureDesc(lcevc->decoder, picture, &desc);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
out->crop_top = desc.cropTop;
out->crop_bottom = desc.cropBottom;
out->crop_left = desc.cropLeft;
out->crop_right = desc.cropRight;
out->sample_aspect_ratio.num = desc.sampleAspectRatioNum;
out->sample_aspect_ratio.den = desc.sampleAspectRatioDen;
out->width = desc.width + out->crop_left + out->crop_right;
out->height = desc.height + out->crop_top + out->crop_bottom;
res = LCEVC_FreePicture(lcevc->decoder, picture);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
return 0;
}
static int lcevc_receive_frame(void *logctx, FFLCEVCContext *lcevc, AVFrame *out)
{
LCEVC_PictureHandle picture;
LCEVC_ReturnCode res;
int ret;
ret = generate_output(logctx, lcevc, out);
if (ret < 0)
return ret;
while (1) {
res = LCEVC_ReceiveDecoderBase (lcevc->decoder, &picture);
if (res != LCEVC_Success && res != LCEVC_Again)
return AVERROR_EXTERNAL;
if (res == LCEVC_Again)
break;
res = LCEVC_FreePicture(lcevc->decoder, picture);
if (res != LCEVC_Success)
return AVERROR_EXTERNAL;
}
return 0;
}
static void event_callback(LCEVC_DecoderHandle dec, LCEVC_Event event,
LCEVC_PictureHandle pic, const LCEVC_DecodeInformation *info,
const uint8_t *data, uint32_t size, void *logctx)
{
switch (event) {
case LCEVC_Log:
av_log(logctx, AV_LOG_INFO, "%s\n", data);
break;
default:
break;
}
}
static void lcevc_free(FFRefStructOpaque unused, void *obj)
{
FFLCEVCContext *lcevc = obj;
if (lcevc->initialized)
LCEVC_DestroyDecoder(lcevc->decoder);
memset(lcevc, 0, sizeof(*lcevc));
}
#endif
static int lcevc_init(FFLCEVCContext *lcevc, void *logctx)
{
#if CONFIG_LIBLCEVC_DEC
LCEVC_AccelContextHandle dummy = { 0 };
const int32_t event = LCEVC_Log;
#endif
if (lcevc->initialized)
return 0;
#if CONFIG_LIBLCEVC_DEC
if (LCEVC_CreateDecoder(&lcevc->decoder, dummy) != LCEVC_Success) {
av_log(logctx, AV_LOG_ERROR, "Failed to create LCEVC decoder\n");
return AVERROR_EXTERNAL;
}
LCEVC_ConfigureDecoderInt(lcevc->decoder, "log_level", 4);
LCEVC_ConfigureDecoderIntArray(lcevc->decoder, "events", 1, &event);
LCEVC_SetDecoderEventCallback(lcevc->decoder, event_callback, logctx);
if (LCEVC_InitializeDecoder(lcevc->decoder) != LCEVC_Success) {
av_log(logctx, AV_LOG_ERROR, "Failed to initialize LCEVC decoder\n");
LCEVC_DestroyDecoder(lcevc->decoder);
return AVERROR_EXTERNAL;
}
#endif
lcevc->initialized = 1;
return 0;
}
int ff_lcevc_process(void *logctx, AVFrame *frame)
{
FrameDecodeData *fdd = (FrameDecodeData*)frame->private_ref->data;
FFLCEVCContext *lcevc = fdd->post_process_opaque;
int ret;
if (!lcevc->initialized) {
ret = lcevc_init(lcevc, logctx);
if (ret < 0)
return ret;
}
#if CONFIG_LIBLCEVC_DEC
ret = lcevc_send_frame(logctx, lcevc, frame);
if (ret)
return ret < 0 ? ret : 0;
lcevc_receive_frame(logctx, lcevc, frame);
if (ret < 0)
return ret;
av_frame_remove_side_data(frame, AV_FRAME_DATA_LCEVC);
#endif
return 0;
}
int ff_lcevc_alloc(FFLCEVCContext **plcevc)
{
FFLCEVCContext *lcevc = NULL;
#if CONFIG_LIBLCEVC_DEC
lcevc = ff_refstruct_alloc_ext(sizeof(*lcevc), 0, NULL, lcevc_free);
if (!lcevc)
return AVERROR(ENOMEM);
#endif
*plcevc = lcevc;
return 0;
}
void ff_lcevc_unref(void *opaque)
{
ff_refstruct_unref(&opaque);
}