mirror of https://git.ffmpeg.org/ffmpeg.git
320 lines
9.0 KiB
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);
|
|
}
|