mirror of https://github.com/FFmpeg/FFmpeg.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
319 lines
9.0 KiB
319 lines
9.0 KiB
/* |
|
* 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); |
|
}
|
|
|