ijksdl_vout_overlay_ffmpeg.c 10.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
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 79 80 81 82 83
    opaque->frame_buffer  = frame_buffer_ref;
     */

    avcodec_get_frame_defaults(managed_frame);
    avcodec_get_frame_defaults(linked_frame);
    AVPicture *pic = (AVPicture *) managed_frame;
    avpicture_fill(pic, NULL, format, width, height);
    opaque->managed_frame = managed_frame;
    opaque->linked_frame  = linked_frame;
    return managed_frame;
}
84

85 86 87 88
static AVFrame *opaque_obtain_managed_frame_buffer(SDL_VoutOverlay_Opaque* opaque)
{
    if (opaque->frame_buffer != NULL)
        return opaque->managed_frame;
89

90 91 92 93
    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)
94 95
        return NULL;

96 97 98 99
    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;
100 101 102 103
}

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

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

B
bbcallen 已提交
112 113
    if (opaque->managed_frame)
        av_frame_free(&opaque->managed_frame);
114

115 116 117 118 119
    if (opaque->linked_frame) {
        av_frame_unref(opaque->linked_frame);
        av_frame_free(&opaque->linked_frame);
    }

120 121
    if (opaque->frame_buffer)
        av_buffer_unref(&opaque->frame_buffer);
122

123 124 125
    if (opaque->mutex)
        SDL_DestroyMutex(opaque->mutex);

126 127 128
    SDL_VoutOverlay_FreeInternal(overlay);
}

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

140 141 142 143 144 145 146 147 148 149 150 151
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);
}

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

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

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

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

Z
Zhang Rui 已提交
229 230 231
    overlay->free_l = overlay_free_l;
    overlay->lock = overlay_lock;
    overlay->unlock = overlay_unlock;
Z
Zhang Rui 已提交
232

Z
Zhang Rui 已提交
233
    return overlay;
Z
Zhang Rui 已提交
234

Z
Zhang Rui 已提交
235 236 237
    fail:
    overlay_free_l(overlay);
    return NULL;
Z
Zhang Rui 已提交
238
}
Z
Zhang Rui 已提交
239

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

249 250
    av_frame_unref(opaque->linked_frame);

Z
Zhang Rui 已提交
251
    int need_swap_uv = 0;
252
    int use_linked_frame = 0;
Z
Zhang Rui 已提交
253 254 255
    enum AVPixelFormat dst_format = AV_PIX_FMT_NONE;
    switch (overlay->format) {
    case SDL_FCC_YV12:
Z
Zhang Rui 已提交
256 257 258
        need_swap_uv = 1;
        // no break;
    case SDL_FCC_I420:
259 260 261
        if (frame->format == AV_PIX_FMT_YUV420P || frame->format == AV_PIX_FMT_YUVJ420P) {
            // ALOGE("direct draw frame");
            use_linked_frame = 1;
262
            dst_format = frame->format;
263 264
        } else {
            // ALOGE("copy draw frame");
265
            dst_format = AV_PIX_FMT_YUV420P;
Z
Zhang Rui 已提交
266
        }
Z
Zhang Rui 已提交
267 268 269 270
        break;
    case SDL_FCC_RV32:
        dst_format = AV_PIX_FMT_0BGR32;
        break;
271 272 273
    case SDL_FCC_RV24:
        dst_format = AV_PIX_FMT_RGB24;
        break;
Z
Zhang Rui 已提交
274 275 276 277 278 279 280 281 282
    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;
    }

283 284 285 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

    // 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
315 316 317
    if (use_linked_frame) {
        // do nothing
    } else if (ijk_image_convert(frame->width, frame->height,
318
        dst_format, swscale_dst_pic.data, swscale_dst_pic.linesize,
319
        frame->format, (const uint8_t**) frame->data, frame->linesize)) {
Z
Zhang Rui 已提交
320
        *p_sws_ctx = sws_getCachedContext(*p_sws_ctx,
321
            frame->width, frame->height, frame->format, frame->width, frame->height,
Z
Zhang Rui 已提交
322 323 324 325 326 327
            dst_format, sws_flags, NULL, NULL, NULL);
        if (*p_sws_ctx == NULL) {
            ALOGE("sws_getCachedContext failed");
            return -1;
        }

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

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

Z
Zhang Rui 已提交
337
    // TODO: 9 draw black if overlay is larger than screen
Z
Zhang Rui 已提交
338 339
    return 0;
}