ijksdl_vout_overlay_ffmpeg.c 10.6 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
static AVFrame *opaque_setup_frame(SDL_VoutOverlay_Opaque* opaque, enum AVPixelFormat format, int width, int height)
52
{
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
    AVFrame *managed_frame = av_frame_alloc();
    if (!managed_frame) {
        return NULL;
    }

    AVFrame *linked_frame = av_frame_alloc();
    if (!linked_frame) {
        av_frame_free(&managed_frame);
        return NULL;
    }

    /*-
     * Lazily allocate frame buffer in opaque_obtain_managed_frame_buffer
     *
     * For refererenced frame management, we use buffer allocated by decoder
     *
69
    int frame_bytes = avpicture_get_size(format, width, height);
70 71
    AVBufferRef *frame_buffer_ref = av_buffer_alloc(frame_bytes);
    if (!frame_buffer_ref)
72
        return NULL;
73 74 75 76 77 78
    opaque->frame_buffer  = frame_buffer_ref;
     */

    avcodec_get_frame_defaults(managed_frame);
    avcodec_get_frame_defaults(linked_frame);
    AVPicture *pic = (AVPicture *) managed_frame;
79 80 81
    managed_frame->format = format;
    managed_frame->width  = width;
    managed_frame->height = height;
82 83 84 85 86
    avpicture_fill(pic, NULL, format, width, height);
    opaque->managed_frame = managed_frame;
    opaque->linked_frame  = linked_frame;
    return managed_frame;
}
87

88 89 90 91
static AVFrame *opaque_obtain_managed_frame_buffer(SDL_VoutOverlay_Opaque* opaque)
{
    if (opaque->frame_buffer != NULL)
        return opaque->managed_frame;
92

93 94 95 96
    AVFrame *managed_frame = opaque->managed_frame;
    int frame_bytes = avpicture_get_size(managed_frame->format, managed_frame->width, managed_frame->height);
    AVBufferRef *frame_buffer_ref = av_buffer_alloc(frame_bytes);
    if (!frame_buffer_ref)
97 98
        return NULL;

99 100 101 102
    AVPicture *pic = (AVPicture *) managed_frame;
    avpicture_fill(pic, frame_buffer_ref->data, managed_frame->format, managed_frame->width, managed_frame->height);
    opaque->frame_buffer  = frame_buffer_ref;
    return opaque->managed_frame;
103 104 105 106
}

static void overlay_free_l(SDL_VoutOverlay *overlay)
{
Z
Zhang Rui 已提交
107
    ALOGE("SDL_Overlay(ffmpeg): overlay_free_l(%p)\n", overlay);
108 109 110 111 112 113 114
    if (!overlay)
        return;

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

B
bbcallen 已提交
115 116
    if (opaque->managed_frame)
        av_frame_free(&opaque->managed_frame);
117

118 119 120 121 122
    if (opaque->linked_frame) {
        av_frame_unref(opaque->linked_frame);
        av_frame_free(&opaque->linked_frame);
    }

123 124
    if (opaque->frame_buffer)
        av_buffer_unref(&opaque->frame_buffer);
125

126 127 128
    if (opaque->mutex)
        SDL_DestroyMutex(opaque->mutex);

129 130 131
    SDL_VoutOverlay_FreeInternal(overlay);
}

132
static void overlay_fill(SDL_VoutOverlay *overlay, AVFrame *frame, int planes)
133 134 135 136 137 138 139 140 141 142
{
    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];
    }
}

143 144 145 146 147 148 149 150 151 152 153 154
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);
}

155
SDL_VoutOverlay *SDL_VoutFFmpeg_CreateOverlay(int width, int height, Uint32 format, SDL_Vout *display)
156
{
Z
Zhang Rui 已提交
157
    SDLTRACE("SDL_VoutFFmpeg_CreateOverlay(w=%d, h=%d, fmt=%.4s(0x%x, dp=%p)\n",
158
        width, height, (const char*) &format, format, display);
159
    SDL_VoutOverlay *overlay = SDL_VoutOverlay_CreateInternal(sizeof(SDL_VoutOverlay_Opaque));
Z
Zhang Rui 已提交
160
    if (!overlay) {
Z
Zhang Rui 已提交
161
        ALOGE("overlay allocation failed");
162
        return NULL;
Z
Zhang Rui 已提交
163
    }
164 165

    SDL_VoutOverlay_Opaque *opaque = overlay->opaque;
Z
Zhang Rui 已提交
166
    overlay->format = format;
167 168
    overlay->pitches = opaque->pitches;
    overlay->pixels = opaque->pixels;
Z
Zhang Rui 已提交
169 170
    overlay->w = width;
    overlay->h = height;
171

Z
Zhang Rui 已提交
172
    enum AVPixelFormat ff_format = AV_PIX_FMT_NONE;
173
    int buf_width = width;
Z
Zhang Rui 已提交
174
    int buf_height = height;
175
    switch (format) {
Z
Zhang Rui 已提交
176
    case SDL_FCC_I420:
177
    case SDL_FCC_YV12: {
Z
Zhang Rui 已提交
178
        ff_format = AV_PIX_FMT_YUV420P;
179 180 181
        // FIXME: need runtime config
#if defined(__ANDROID__)
        // 16 bytes align pitch for arm-neon image-convert
Z
Zhang Rui 已提交
182
        buf_width = IJKALIGN(width, 16); // 1 bytes per pixel for Y-plane
183 184
#elif defined(__APPLE__)
        // 2^n align for width
B
bbcallen 已提交
185 186 187
        buf_width = width;
        if (width > 0)
            buf_width = 1 << (sizeof(int) * 8 - __builtin_clz(width));
188 189 190
#else
        buf_width = IJKALIGN(width, 16); // unknown platform
#endif
191
        opaque->planes = 3;
192 193
        break;
    }
Z
Zhang Rui 已提交
194
    case SDL_FCC_RV16: {
Z
Zhang Rui 已提交
195
        ff_format = AV_PIX_FMT_RGB565;
Z
Zhang Rui 已提交
196
        buf_width = IJKALIGN(width, 8); // 2 bytes per pixel
197
        opaque->planes = 1;
198 199
        break;
    }
200 201 202 203 204 205 206 207 208 209 210 211 212
    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 已提交
213
    case SDL_FCC_RV32: {
Z
Zhang Rui 已提交
214
        ff_format = AV_PIX_FMT_0BGR32;
Z
Zhang Rui 已提交
215
        buf_width = IJKALIGN(width, 4); // 4 bytes per pixel
216
        opaque->planes = 1;
217
        break;
218
    }
219
    default:
Z
Zhang Rui 已提交
220
        ALOGE("SDL_VoutFFmpeg_CreateOverlay(...): unknown format %.4s(0x%x)\n", (char*)&format, format);
Z
Zhang Rui 已提交
221
        goto fail;
222 223
    }

224
    opaque->managed_frame = opaque_setup_frame(opaque, ff_format, buf_width, buf_height);
B
bbcallen 已提交
225
    if (!opaque->managed_frame) {
Z
Zhang Rui 已提交
226
        ALOGE("overlay->opaque->frame allocation failed\n");
Z
Zhang Rui 已提交
227
        goto fail;
228
    }
Z
Zhang Rui 已提交
229
    opaque->mutex = SDL_CreateMutex();
B
bbcallen 已提交
230
    overlay_fill(overlay, opaque->managed_frame, opaque->planes);
231

Z
Zhang Rui 已提交
232 233 234
    overlay->free_l = overlay_free_l;
    overlay->lock = overlay_lock;
    overlay->unlock = overlay_unlock;
Z
Zhang Rui 已提交
235

Z
Zhang Rui 已提交
236
    return overlay;
Z
Zhang Rui 已提交
237

Z
Zhang Rui 已提交
238 239 240
    fail:
    overlay_free_l(overlay);
    return NULL;
Z
Zhang Rui 已提交
241
}
Z
Zhang Rui 已提交
242

243 244
int SDL_VoutFFmpeg_ConvertFrame(
    SDL_VoutOverlay *overlay, AVFrame *frame,
Z
Zhang Rui 已提交
245 246 247 248
    struct SwsContext **p_sws_ctx, int sws_flags)
{
    assert(overlay);
    assert(p_sws_ctx);
Z
Zhang Rui 已提交
249
    SDL_VoutOverlay_Opaque *opaque = overlay->opaque;
250
    AVPicture swscale_dst_pic = { { 0 } };
251

252 253
    av_frame_unref(opaque->linked_frame);

Z
Zhang Rui 已提交
254
    int need_swap_uv = 0;
255
    int use_linked_frame = 0;
Z
Zhang Rui 已提交
256 257 258
    enum AVPixelFormat dst_format = AV_PIX_FMT_NONE;
    switch (overlay->format) {
    case SDL_FCC_YV12:
Z
Zhang Rui 已提交
259 260 261
        need_swap_uv = 1;
        // no break;
    case SDL_FCC_I420:
262 263 264
        if (frame->format == AV_PIX_FMT_YUV420P || frame->format == AV_PIX_FMT_YUVJ420P) {
            // ALOGE("direct draw frame");
            use_linked_frame = 1;
265
            dst_format = frame->format;
266 267
        } else {
            // ALOGE("copy draw frame");
268
            dst_format = AV_PIX_FMT_YUV420P;
Z
Zhang Rui 已提交
269
        }
Z
Zhang Rui 已提交
270 271 272 273
        break;
    case SDL_FCC_RV32:
        dst_format = AV_PIX_FMT_0BGR32;
        break;
274 275 276
    case SDL_FCC_RV24:
        dst_format = AV_PIX_FMT_RGB24;
        break;
Z
Zhang Rui 已提交
277 278 279 280 281 282 283 284 285
    case SDL_FCC_RV16:
        dst_format = AV_PIX_FMT_RGB565;
        break;
    default:
        ALOGE("SDL_VoutFFmpeg_ConvertPicture: unexpected overlay format %s(%d)",
            (char*)&overlay->format, overlay->format);
        return -1;
    }

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317

    // setup frame
    if (use_linked_frame) {
        // linked frame
        av_frame_ref(opaque->linked_frame, frame);

        overlay_fill(overlay, opaque->linked_frame, opaque->planes);

        if (need_swap_uv)
            FFSWAP(Uint8*, overlay->pixels[1], overlay->pixels[2]);
    } else {
        // managed frame
        AVFrame* managed_frame = opaque_obtain_managed_frame_buffer(opaque);
        if (!managed_frame) {
            ALOGE("OOM in opaque_obtain_managed_frame_buffer");
            return -1;
        }

        // setup frame managed
        for (int i = 0; i < overlay->planes; ++i) {
            swscale_dst_pic.data[i] = overlay->pixels[i];
            swscale_dst_pic.linesize[i] = overlay->pitches[i];
        }

        overlay_fill(overlay, opaque->managed_frame, opaque->planes);

        if (need_swap_uv)
            FFSWAP(Uint8*, swscale_dst_pic.data[1], swscale_dst_pic.data[2]);
    }


    // swscale / direct draw
318 319 320
    if (use_linked_frame) {
        // do nothing
    } else if (ijk_image_convert(frame->width, frame->height,
321
        dst_format, swscale_dst_pic.data, swscale_dst_pic.linesize,
322
        frame->format, (const uint8_t**) frame->data, frame->linesize)) {
Z
Zhang Rui 已提交
323
        *p_sws_ctx = sws_getCachedContext(*p_sws_ctx,
324
            frame->width, frame->height, frame->format, frame->width, frame->height,
Z
Zhang Rui 已提交
325 326 327 328 329 330
            dst_format, sws_flags, NULL, NULL, NULL);
        if (*p_sws_ctx == NULL) {
            ALOGE("sws_getCachedContext failed");
            return -1;
        }

331
        sws_scale(*p_sws_ctx, (const uint8_t**) frame->data, frame->linesize,
332
            0, frame->height, swscale_dst_pic.data, swscale_dst_pic.linesize);
Z
Zhang Rui 已提交
333 334 335

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

Z
Zhang Rui 已提交
340
    // TODO: 9 draw black if overlay is larger than screen
Z
Zhang Rui 已提交
341 342
    return 0;
}