summaryrefslogtreecommitdiff
path: root/src/codec/ffmpeg/scaler.c
blob: 0cd7fcb5bac30729dc84a50782aa1eca4cdb3d4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#define AL_LOG_SECTION "ff_scaler"
#include <al/log.h>
#include <al/lib.h>
#include <libavutil/imgutils.h>

#include "scaler.h"

static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, s32 width, s32 height)
{
    AVFrame *frame = av_frame_alloc();
    if (!frame) return NULL;
    s32 ret = av_image_alloc(frame->data, frame->linesize, width, height, pix_fmt, 32);
    if (ret < 0) {
        log_error("Failed to allocate picture buffer (%s).", av_err2str(ret));
        return NULL;
    }
    /*
    s32 size = av_image_get_buffer_size(pix_fmt, width, height, 1);
    u8 *buffer = (u8 *)av_malloc(size);
    if (!buffer) {
        av_free(frame);
        return NULL;
    }
    s32 ret = av_image_fill_arrays(frame->data, frame->linesize, buffer, pix_fmt, width, height, 1);
    if (ret < 0) {
        log_error("Failed to allocate picture buffer (%s).", av_err2str(ret));
        av_free(frame);
        return NULL;
    }
    */
    frame->width = width;
    frame->height = height;
    frame->format = pix_fmt;
    return frame;
}

static bool ff_scaler_init(struct camu_scaler *scale, struct camu_scaler_format *fmt)
{
    struct camu_ff_scaler *av = (struct camu_ff_scaler *)scale;
    al_assert(fmt->scaler_needed);

    av->fmt = *fmt;

    av->scaler_context = sws_getContext(
        fmt->in.width, fmt->in.height, (enum AVPixelFormat)fmt->in.format,
        fmt->req.width, fmt->req.height, (enum AVPixelFormat)fmt->req.format,
        SWS_BITEXACT, NULL, NULL, NULL);
    if (!av->scaler_context) {
        log_error("Failed to create scaler context.");
        return false;
    }

    struct camu_codec_frame *frame = &av->frame;
    frame->av.frame = alloc_picture((enum AVPixelFormat)fmt->req.format, fmt->req.width, fmt->req.height);
    if (!frame->av.frame) {
        log_error("Failed to allocate frame.");
        return false;
    }

    return true;
}

static bool ff_scaler_scale(struct camu_scaler *scale, const u8 **in_slice, s32 *in_strides)
{
    struct camu_ff_scaler *av = (struct camu_ff_scaler *)scale;
    AVFrame *frame = av->frame.av.frame;
    struct camu_scaler_format *fmt = &av->fmt;
    s32 slice_height = (s32)fmt->in.height;
    s32 ret = sws_scale(av->scaler_context, in_slice, in_strides, 0,
        slice_height, frame->data, frame->linesize);
    if (ret != (s32)fmt->req.height) {
        log_error("Failed to scale frame (%s).", av_err2str(ret));
        return false;
    }
    return true;
}

static struct camu_codec_frame *ff_scaler_get_frame(struct camu_scaler *scale)
{
    struct camu_ff_scaler *av = (struct camu_ff_scaler *)scale;
    return &av->frame;
}

static void ff_scaler_free(struct camu_scaler **scale)
{
    struct camu_ff_scaler *av = (struct camu_ff_scaler *)*scale;
    struct camu_codec_frame *frame = &av->frame;
    if (frame->av.frame) {
        av_freep(&frame->av.frame->data[0]);
        av_frame_free(&frame->av.frame);
    }
    if (av->scaler_context) {
        sws_freeContext(av->scaler_context);
        av->scaler_context = NULL;
    }
    al_free(av);
    *scale = NULL;
}

struct camu_scaler *camu_ff_scaler_create(void)
{
    struct camu_ff_scaler *av = al_alloc_object(struct camu_ff_scaler);
    av->scale.init = ff_scaler_init;
    av->scale.scale = ff_scaler_scale;
    av->scale.get_frame = ff_scaler_get_frame;
    av->scale.free = ff_scaler_free;
    return (struct camu_scaler *)av;
}