ijksdl_vout_overlay_ffmpeg.c 9.5 KB
Newer Older
1
/*****************************************************************************
Z
Zhang Rui 已提交
2
 * ijksdl_vout_overlay_ffmpeg.c
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *****************************************************************************
 *
 * copyright (c) 2013 Zhang Rui <bbcallen@gmail.com>
 *
 * This file is part of ijkPlayer.
 *
 * ijkPlayer 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.
 *
 * ijkPlayer 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 ijkPlayer; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

Z
Zhang Rui 已提交
24
#include "ijksdl_vout_overlay_ffmpeg.h"
25

Z
Zhang Rui 已提交
26
#include <assert.h>
27 28 29
#include "../ijksdl_stdinc.h"
#include "../ijksdl_mutex.h"
#include "../ijksdl_vout_internal.h"
Z
Zhang Rui 已提交
30
#include "../ijksdl_video.h"
Z
Zhang Rui 已提交
31
#include "ijksdl_inc_ffmpeg.h"
Z
Zhang Rui 已提交
32
#include "ijksdl_image_convert.h"
33 34

typedef struct SDL_VoutOverlay_Opaque {
35 36
    SDL_mutex *mutex;

B
bbcallen 已提交
37
    AVFrame *managed_frame;
38 39 40 41
    AVBufferRef *frame_buffer;
    int planes;

    AVFrame *linked_frame;
42 43 44

    Uint16 pitches[AV_NUM_DATA_POINTERS];
    Uint8 *pixels[AV_NUM_DATA_POINTERS];
Z
Zhang Rui 已提交
45 46

    int no_neon_warned;
47 48 49
} SDL_VoutOverlay_Opaque;

/* Always assume a linesize alignment of 1 here */
Z
Zhang Rui 已提交
50
// TODO: 9 alignment to speed up memcpy when display
51 52 53
static AVFrame *alloc_avframe(SDL_VoutOverlay_Opaque* opaque, enum AVPixelFormat format, int width, int height)
{
    int frame_bytes = avpicture_get_size(format, width, height);
54 55
    AVBufferRef *frame_buffer_ref = av_buffer_alloc(frame_bytes);
    if (!frame_buffer_ref)
56 57
        return NULL;

58
    AVFrame *frame = av_frame_alloc();
59
    if (!frame) {
60
        av_buffer_unref(&frame_buffer_ref);
61 62 63
        return NULL;
    }

64 65 66 67 68 69 70
    AVFrame *linked_frame = av_frame_alloc();
    if (!frame) {
        av_frame_free(&frame);
        av_buffer_unref(&frame_buffer_ref);
        return NULL;
    }

71
    AVPicture *pic = (AVPicture *) frame;
72
    avcodec_get_frame_defaults(frame);
73 74
    avpicture_fill(pic, frame_buffer_ref->data, format, width, height);
    opaque->frame_buffer = frame_buffer_ref;
75
    opaque->linked_frame = linked_frame;
76 77 78 79 80
    return frame;
}

static void overlay_free_l(SDL_VoutOverlay *overlay)
{
Z
Zhang Rui 已提交
81
    ALOGE("SDL_Overlay(ffmpeg): overlay_free_l(%p)", overlay);
82 83 84 85 86 87 88
    if (!overlay)
        return;

    SDL_VoutOverlay_Opaque *opaque = overlay->opaque;
    if (!opaque)
        return;

B
bbcallen 已提交
89 90
    if (opaque->managed_frame)
        av_frame_free(&opaque->managed_frame);
91

92 93 94 95 96
    if (opaque->linked_frame) {
        av_frame_unref(opaque->linked_frame);
        av_frame_free(&opaque->linked_frame);
    }

97 98
    if (opaque->frame_buffer)
        av_buffer_unref(&opaque->frame_buffer);
99

100 101 102
    if (opaque->mutex)
        SDL_DestroyMutex(opaque->mutex);

103 104 105
    SDL_VoutOverlay_FreeInternal(overlay);
}

106
static void overlay_fill(SDL_VoutOverlay *overlay, AVFrame *frame, int planes)
107 108 109 110 111 112 113 114 115 116
{
    AVPicture *pic = (AVPicture *) frame;
    overlay->planes = planes;

    for (int i = 0; i < AV_NUM_DATA_POINTERS; ++i) {
        overlay->pixels[i] = pic->data[i];
        overlay->pitches[i] = pic->linesize[i];
    }
}

117 118 119 120 121 122 123 124 125 126 127 128
static int overlay_lock(SDL_VoutOverlay *overlay)
{
    SDL_VoutOverlay_Opaque *opaque = overlay->opaque;
    return SDL_LockMutex(opaque->mutex);
}

static int overlay_unlock(SDL_VoutOverlay *overlay)
{
    SDL_VoutOverlay_Opaque *opaque = overlay->opaque;
    return SDL_UnlockMutex(opaque->mutex);
}

129
SDL_VoutOverlay *SDL_VoutFFmpeg_CreateOverlay(int width, int height, Uint32 format, SDL_Vout *display)
130
{
131 132
    SDLTRACE("SDL_VoutFFmpeg_CreateOverlay(w=%d, h=%d, fmt=%.4s(0x%x, dp=%p)",
        width, height, (const char*) &format, format, display);
133
    SDL_VoutOverlay *overlay = SDL_VoutOverlay_CreateInternal(sizeof(SDL_VoutOverlay_Opaque));
Z
Zhang Rui 已提交
134
    if (!overlay) {
Z
Zhang Rui 已提交
135
        ALOGE("overlay allocation failed");
136
        return NULL;
Z
Zhang Rui 已提交
137
    }
138 139

    SDL_VoutOverlay_Opaque *opaque = overlay->opaque;
Z
Zhang Rui 已提交
140
    overlay->format = format;
141 142
    overlay->pitches = opaque->pitches;
    overlay->pixels = opaque->pixels;
Z
Zhang Rui 已提交
143 144
    overlay->w = width;
    overlay->h = height;
145

Z
Zhang Rui 已提交
146
    enum AVPixelFormat ff_format = AV_PIX_FMT_NONE;
147
    int buf_width = width;
Z
Zhang Rui 已提交
148
    int buf_height = height;
149
    switch (format) {
Z
Zhang Rui 已提交
150
    case SDL_FCC_I420:
151
    case SDL_FCC_YV12: {
Z
Zhang Rui 已提交
152
        ff_format = AV_PIX_FMT_YUV420P;
153 154 155
        // FIXME: need runtime config
#if defined(__ANDROID__)
        // 16 bytes align pitch for arm-neon image-convert
Z
Zhang Rui 已提交
156
        buf_width = IJKALIGN(width, 16); // 1 bytes per pixel for Y-plane
157 158
#elif defined(__APPLE__)
        // 2^n align for width
B
bbcallen 已提交
159 160 161
        buf_width = width;
        if (width > 0)
            buf_width = 1 << (sizeof(int) * 8 - __builtin_clz(width));
162 163 164
#else
        buf_width = IJKALIGN(width, 16); // unknown platform
#endif
165
        opaque->planes = 3;
166 167
        break;
    }
Z
Zhang Rui 已提交
168
    case SDL_FCC_RV16: {
Z
Zhang Rui 已提交
169
        ff_format = AV_PIX_FMT_RGB565;
Z
Zhang Rui 已提交
170
        buf_width = IJKALIGN(width, 8); // 2 bytes per pixel
171
        opaque->planes = 1;
172 173
        break;
    }
174 175 176 177 178 179 180 181 182 183 184 185 186
    case SDL_FCC_RV24: {
        ff_format = AV_PIX_FMT_RGB24;
#if defined(__ANDROID__)
        // 16 bytes align pitch for arm-neon image-convert
        buf_width = IJKALIGN(width, 16); // 1 bytes per pixel for Y-plane
#elif defined(__APPLE__)
        buf_width = width;
#else
        buf_width = IJKALIGN(width, 16); // unknown platform
#endif
        opaque->planes = 1;
        break;
    }
Z
Zhang Rui 已提交
187
    case SDL_FCC_RV32: {
Z
Zhang Rui 已提交
188
        ff_format = AV_PIX_FMT_0BGR32;
Z
Zhang Rui 已提交
189
        buf_width = IJKALIGN(width, 4); // 4 bytes per pixel
190
        opaque->planes = 1;
191
        break;
192
    }
193
    default:
194
        ALOGE("SDL_VoutFFmpeg_CreateOverlay(...): unknown format %.4s(0x%x)", (char*)&format, format);
Z
Zhang Rui 已提交
195
        goto fail;
196 197
    }

B
bbcallen 已提交
198 199
    opaque->managed_frame = alloc_avframe(opaque, ff_format, buf_width, buf_height);
    if (!opaque->managed_frame) {
Z
Zhang Rui 已提交
200 201
        ALOGE("overlay->opaque->frame allocation failed");
        goto fail;
202
    }
Z
Zhang Rui 已提交
203
    opaque->mutex = SDL_CreateMutex();
B
bbcallen 已提交
204
    overlay_fill(overlay, opaque->managed_frame, opaque->planes);
205

Z
Zhang Rui 已提交
206 207 208
    overlay->free_l = overlay_free_l;
    overlay->lock = overlay_lock;
    overlay->unlock = overlay_unlock;
Z
Zhang Rui 已提交
209

Z
Zhang Rui 已提交
210
    return overlay;
Z
Zhang Rui 已提交
211

Z
Zhang Rui 已提交
212 213 214
    fail:
    overlay_free_l(overlay);
    return NULL;
Z
Zhang Rui 已提交
215
}
Z
Zhang Rui 已提交
216

217 218
int SDL_VoutFFmpeg_ConvertFrame(
    SDL_VoutOverlay *overlay, AVFrame *frame,
Z
Zhang Rui 已提交
219 220 221 222
    struct SwsContext **p_sws_ctx, int sws_flags)
{
    assert(overlay);
    assert(p_sws_ctx);
Z
Zhang Rui 已提交
223
    SDL_VoutOverlay_Opaque *opaque = overlay->opaque;
Z
Zhang Rui 已提交
224 225
    AVPicture dest_pic = { { 0 } };

226 227 228 229 230
    for (int i = 0; i < overlay->planes; ++i) {
        dest_pic.data[i] = overlay->pixels[i];
        dest_pic.linesize[i] = overlay->pitches[i];
    }

231 232
    av_frame_unref(opaque->linked_frame);

Z
Zhang Rui 已提交
233
    int need_swap_uv = 0;
234
    int use_linked_frame = 0;
Z
Zhang Rui 已提交
235 236 237
    enum AVPixelFormat dst_format = AV_PIX_FMT_NONE;
    switch (overlay->format) {
    case SDL_FCC_YV12:
Z
Zhang Rui 已提交
238 239 240
        need_swap_uv = 1;
        // no break;
    case SDL_FCC_I420:
241 242 243 244 245 246 247
        if (frame->format == AV_PIX_FMT_YUV420P || frame->format == AV_PIX_FMT_YUVJ420P) {
            // ALOGE("direct draw frame");
            use_linked_frame = 1;
            av_frame_ref(opaque->linked_frame, frame);
            overlay_fill(overlay, opaque->linked_frame, opaque->planes);
        } else {
            // ALOGE("copy draw frame");
B
bbcallen 已提交
248
            overlay_fill(overlay, opaque->managed_frame, opaque->planes);
Z
Zhang Rui 已提交
249 250
            dest_pic.data[1] = overlay->pixels[1];
            dest_pic.data[2] = overlay->pixels[2];
251
            dst_format = AV_PIX_FMT_YUV420P;
Z
Zhang Rui 已提交
252 253 254 255 256 257 258 259
        }

        if (need_swap_uv) {
            if (use_linked_frame) {
                FFSWAP(Uint8*, overlay->pixels[1], overlay->pixels[2]);
            } else {
                FFSWAP(Uint8*, dest_pic.data[1], dest_pic.data[2]);
            }
260
        }
Z
Zhang Rui 已提交
261 262
        break;
    case SDL_FCC_RV32:
B
bbcallen 已提交
263
        overlay_fill(overlay, opaque->managed_frame, opaque->planes);
Z
Zhang Rui 已提交
264 265
        dst_format = AV_PIX_FMT_0BGR32;
        break;
266
    case SDL_FCC_RV24:
B
bbcallen 已提交
267
        overlay_fill(overlay, opaque->managed_frame, opaque->planes);
268 269
        dst_format = AV_PIX_FMT_RGB24;
        break;
Z
Zhang Rui 已提交
270
    case SDL_FCC_RV16:
B
bbcallen 已提交
271
        overlay_fill(overlay, opaque->managed_frame, opaque->planes);
Z
Zhang Rui 已提交
272 273 274 275 276 277 278 279
        dst_format = AV_PIX_FMT_RGB565;
        break;
    default:
        ALOGE("SDL_VoutFFmpeg_ConvertPicture: unexpected overlay format %s(%d)",
            (char*)&overlay->format, overlay->format);
        return -1;
    }

280 281 282
    if (use_linked_frame) {
        // do nothing
    } else if (ijk_image_convert(frame->width, frame->height,
Z
Zhang Rui 已提交
283
        dst_format, dest_pic.data, dest_pic.linesize,
284
        frame->format, (const uint8_t**) frame->data, frame->linesize)) {
Z
Zhang Rui 已提交
285
        *p_sws_ctx = sws_getCachedContext(*p_sws_ctx,
286
            frame->width, frame->height, frame->format, frame->width, frame->height,
Z
Zhang Rui 已提交
287 288 289 290 291 292
            dst_format, sws_flags, NULL, NULL, NULL);
        if (*p_sws_ctx == NULL) {
            ALOGE("sws_getCachedContext failed");
            return -1;
        }

293 294
        sws_scale(*p_sws_ctx, (const uint8_t**) frame->data, frame->linesize,
            0, frame->height, dest_pic.data, dest_pic.linesize);
Z
Zhang Rui 已提交
295 296 297

        if (!opaque->no_neon_warned) {
            opaque->no_neon_warned = 1;
298
            ALOGE("non-neon image convert %s -> %s", av_get_pix_fmt_name(frame->format), av_get_pix_fmt_name(dst_format));
Z
Zhang Rui 已提交
299
        }
Z
Zhang Rui 已提交
300 301
    }

Z
Zhang Rui 已提交
302
    // TODO: 9 draw black if overlay is larger than screen
Z
Zhang Rui 已提交
303 304
    return 0;
}