From ee3ba3fe811498441d3be162af76147b07829ade Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 19 Jun 2013 16:54:59 +0800 Subject: [PATCH] ijksdl: implement YV12 picture copy to native window buffer --- ijkmediaplayer/jni/ijksdl/ijksdl_ffinc.h | 31 ++++ ijkmediaplayer/jni/ijksdl/ijksdl_vout.h | 7 +- .../ijksdl/ijksdl_vout_android_nativewindow.c | 137 +++++++++++++++++- .../jni/ijksdl/ijksdl_vout_ffmpeg.c | 7 +- ijkmediaplayer/jni/ijkutil/ijkutil.h | 8 + 5 files changed, 177 insertions(+), 13 deletions(-) create mode 100644 ijkmediaplayer/jni/ijksdl/ijksdl_ffinc.h diff --git a/ijkmediaplayer/jni/ijksdl/ijksdl_ffinc.h b/ijkmediaplayer/jni/ijksdl/ijksdl_ffinc.h new file mode 100644 index 00000000..bd082511 --- /dev/null +++ b/ijkmediaplayer/jni/ijksdl/ijksdl_ffinc.h @@ -0,0 +1,31 @@ +/* + * ijksdl_ffinc.h + * ffmpeg headers + * + * Copyright (c) 2013 Zhang Rui + * + * 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 + */ + +#ifndef IJKPLAYER__IJKSDL_FFINC_H +#define IJKPLAYER__IJKSDL_FFINC_H + +#include "libavutil/imgutils.h" +#include "libavcodec/avcodec.h" +#include "libswscale/swscale.h" + +#endif diff --git a/ijkmediaplayer/jni/ijksdl/ijksdl_vout.h b/ijkmediaplayer/jni/ijksdl/ijksdl_vout.h index e8c03d8c..e1babd98 100644 --- a/ijkmediaplayer/jni/ijksdl/ijksdl_vout.h +++ b/ijkmediaplayer/jni/ijksdl/ijksdl_vout.h @@ -28,9 +28,6 @@ #include "ijksdl_mutex.h" #include "ijksdl_video.h" -/* bpp=12, 8 bit Y plane followed by 8 bit 2x2 subsampled V and U planes.*/ -#define SDL_FOURCC_PIX__YV12 0x32315659 - typedef struct SDL_VoutOverlay_Opaque SDL_VoutOverlay_Opaque; typedef struct SDL_VoutOverlay SDL_VoutOverlay; typedef struct SDL_VoutOverlay { @@ -50,8 +47,8 @@ typedef struct SDL_VoutOverlay { typedef struct SDL_VoutSurface_Opaque SDL_VoutSurface_Opaque; typedef struct SDL_VoutSurface SDL_VoutSurface; typedef struct SDL_VoutSurface { - int w; - int h; + int w; /* width in pixels */ + int h; /* height in pixels */ Uint32 format; /* fourcc number */ SDL_VoutSurface_Opaque *opaque; diff --git a/ijkmediaplayer/jni/ijksdl/ijksdl_vout_android_nativewindow.c b/ijkmediaplayer/jni/ijksdl/ijksdl_vout_android_nativewindow.c index 5c73a2dc..d0d0ee82 100644 --- a/ijkmediaplayer/jni/ijksdl/ijksdl_vout_android_nativewindow.c +++ b/ijkmediaplayer/jni/ijksdl/ijksdl_vout_android_nativewindow.c @@ -25,6 +25,8 @@ #include #include +#include "ijkutil/ijkutil.h" +#include "ijksdl_ffinc.h" #include "ijksdl_vout.h" #include "ijksdl_vout_internal.h" @@ -61,27 +63,40 @@ static SDL_VoutSurface *vout_set_video_mode_l(SDL_Vout *vout, int w, int h, int int curr_w = 0; int curr_h = 0; int curr_format = 0; + int buf_w = (w + 1) & ~1; + int buf_h = (h + 1) & ~1; + assert(bpp == 0); if (!native_window) return NULL; curr_w = ANativeWindow_getWidth(native_window); curr_h = ANativeWindow_getHeight(native_window); curr_format = ANativeWindow_getFormat(native_window); - if (curr_w == w && curr_h == h) { + if (curr_w == buf_w && curr_h == buf_h) { + surface->w = w; + surface->h = h; surface->format = curr_format; return surface; } - ANativeWindow_setBuffersGeometry(native_window, w, h, curr_format); + ALOGI("vout_set_video_mode_l (w:%d, h:%d, fmt:%d) => (w:%d, h:%d, fmt:%d)", + curr_w, curr_h, curr_format, + buf_w, buf_h, curr_format); + ANativeWindow_setBuffersGeometry(native_window, buf_w, buf_h, curr_format); curr_w = ANativeWindow_getWidth(native_window); curr_h = ANativeWindow_getHeight(native_window); curr_format = ANativeWindow_getFormat(native_window); - if (curr_w == w && curr_h == h) { + if (curr_w == buf_w && curr_h == buf_h) { + surface->w = w; + surface->h = h; surface->format = curr_format; return surface; } + ALOGE("vout_set_video_mode_l (w:%d, h:%d, fmt:%d) => (w:%d, h:%d, fmt:%d)", + curr_w, curr_h, curr_format, + buf_w, buf_h, curr_format); return NULL; } @@ -93,10 +108,122 @@ static SDL_VoutSurface *vout_set_video_mode(SDL_Vout *vout, int w, int h, int bp return surface; } +static void vout_copy_image_yv12(ANativeWindow_Buffer *out_buffer, const SDL_VoutOverlay *overlay) +{ + assert(overlay->format == SDL_YV12_OVERLAY); + assert(overlay->planes == 3); + + int min_height = IJKMIN(out_buffer->height, overlay->h); + int dst_y_pitch = out_buffer->stride; + int dst_c_pitch = IJKALIGN(out_buffer->stride / 2, 16); + int dst_y_size = dst_y_pitch * out_buffer->height; + int dst_c_size = dst_c_pitch * out_buffer->height / 2; + + uint8_t *dst_pixels[] = { + out_buffer->bits, + out_buffer->bits + dst_y_size, + out_buffer->bits + dst_y_size + dst_c_size + }; + uint8_t *dst_planes_size[] = { dst_y_size, dst_c_size, dst_c_size }; + int dst_pitches[] = { dst_y_pitch, dst_c_pitch, dst_c_pitch }; + + for (int i = 0; i < 3; ++i) { + int dst_pitch = dst_pitches[i]; + int src_pitch = overlay->pitches[i]; + uint8_t *dst_pixels = dst_pixels[i]; + const uint8_t *src_pixels = overlay->pixels[i]; + int dst_plane_size = dst_planes_size[i]; + + if (dst_pitch == src_pitch) { + memcpy(dst_pixels, src_pixels, dst_plane_size); + } else { + int bytewidth = IJKMIN(dst_pitch, src_pitch); + av_image_copy_plane(dst_pixels, dst_pitch, src_pixels, src_pitch, bytewidth, min_height); + } + } +} + +static int voud_display_overlay_l(SDL_Vout *vout, SDL_VoutOverlay *overlay) +{ + SDL_Vout_Opaque *opaque = vout->opaque; + ANativeWindow *native_window = opaque->native_window; + int curr_w, curr_h, curr_format; + int retval; + + if (!native_window) + return -1; + + if (!overlay || overlay->w <= 0 || overlay->h <= 0) + return -1; + + int buf_w = (overlay->w + 1) & ~1; + int buf_h = (overlay->h + 1) & ~1; + + curr_w = ANativeWindow_getWidth(native_window); + curr_h = ANativeWindow_getHeight(native_window); + curr_format = ANativeWindow_getFormat(native_window); + if (curr_w != buf_w || + curr_h != buf_h || + curr_format != overlay->format) { + + // correct w, h, format + ALOGI("vout_set_video_mode_l (w:%d, h:%d, fmt:%d) => (w:%d, h:%d, fmt:%d)", + curr_w, curr_h, curr_format, + buf_w, buf_h, overlay->format); + ANativeWindow_setBuffersGeometry(native_window, buf_w, buf_h, overlay->format); + curr_w = ANativeWindow_getWidth(native_window); + curr_h = ANativeWindow_getHeight(native_window); + curr_format = ANativeWindow_getFormat(native_window); + + if (curr_w != buf_w || + curr_h != buf_h || + curr_format != overlay->format) { + ALOGE("unexpected native window (w:%d, h:%d, fmt:%d), expecting (w:%d, h:%d, fmt:%d)", + curr_w, curr_h, curr_format, + buf_w, buf_h, overlay->format); + return -1; + } + } + + ANativeWindow_Buffer out_buffer; + retval = ANativeWindow_lock(native_window, &out_buffer, NULL); + if (retval < 0) { + ALOGE("voud_display_overlay_l: ANativeWindow_lock: failed %d", retval); + return retval; + } + + if (out_buffer.width != buf_w || out_buffer.height != buf_w) { + ALOGE("unexpected native window buffer (w:%d, h:%d, fmt:%d), expecting (w:%d, h:%d, fmt:%d)", + out_buffer.width, out_buffer.height, out_buffer.format, + buf_w, buf_h, overlay->format); + return -1; + } + + int copy_ret = 0; + switch (out_buffer.format) { + case SDL_YV12_OVERLAY: + vout_copy_image_yv12(&out_buffer, overlay); + break; + default: + ALOGE("voud_display_overlay_l: unexpected buffer format: %d", out_buffer.format); + copy_ret = -1; + break; + } + + retval = ANativeWindow_unlockAndPost(native_window); + if (retval < 0) { + ALOGE("voud_display_overlay_l: ANativeWindow_unlockAndPost: failed %d", retval); + return retval; + } + return copy_ret; +} + static int voud_display_overlay(SDL_Vout *vout, SDL_VoutOverlay *overlay) { - // FIXME: implement - return -1; + SDL_LockMutex(vout->mutex); + int retval = voud_display_overlay_l(vout, overlay); + SDL_UnlockMutex(vout->mutex); + return retval; } SDL_Vout *SDL_VoutAndroid_CreateForANativeWindow() diff --git a/ijkmediaplayer/jni/ijksdl/ijksdl_vout_ffmpeg.c b/ijkmediaplayer/jni/ijksdl/ijksdl_vout_ffmpeg.c index f94df50e..81d379ea 100644 --- a/ijkmediaplayer/jni/ijksdl/ijksdl_vout_ffmpeg.c +++ b/ijkmediaplayer/jni/ijksdl/ijksdl_vout_ffmpeg.c @@ -23,8 +23,7 @@ #include "ijksdl_vout_ffmpeg.h" -#include "libavcodec/avcodec.h" -#include "libswscale/swscale.h" +#include "ijksdl_ffinc.h" #include "ijksdl_mutex.h" #include "ijksdl_vout_internal.h" @@ -39,6 +38,7 @@ typedef struct SDL_VoutOverlay_Opaque { } SDL_VoutOverlay_Opaque; /* Always assume a linesize alignment of 1 here */ +// FIXME: alignment static AVFrame *alloc_avframe(SDL_VoutOverlay_Opaque* opaque, enum AVPixelFormat format, int width, int height) { int frame_bytes = avpicture_get_size(format, width, height); @@ -52,8 +52,9 @@ static AVFrame *alloc_avframe(SDL_VoutOverlay_Opaque* opaque, enum AVPixelFormat return NULL; } + AVPicture *pic = (AVPicture *) frame; avcodec_get_frame_defaults(frame); - avpicture_fill((AVPicture *) frame, frame_buf, format, width, height); + avpicture_fill(pic, frame_buf, format, width, height); opaque->frame_buf = frame_buf; return frame; } diff --git a/ijkmediaplayer/jni/ijkutil/ijkutil.h b/ijkmediaplayer/jni/ijkutil/ijkutil.h index 3521478f..ee347195 100644 --- a/ijkmediaplayer/jni/ijkutil/ijkutil.h +++ b/ijkmediaplayer/jni/ijkutil/ijkutil.h @@ -31,4 +31,12 @@ #define IJKMAX(a, b) ((a) > (b) ? (a) : (b)) #endif +#ifndef IJKMIN +#define IJKMIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef IJKALIGN +#define IJKALIGN(x, align) ((( x ) + (align) - 1) / (align) * (align)) +#endif + #endif -- GitLab