static int android_render_yv12_on_yv12(ANativeWindow_Buffer *out_buffer, const SDL_VoutOverlay *overlay) { // SDLTRACE("SDL_VoutAndroid: android_render_yv12_on_yv12(%p)", overlay); assert(overlay->format == SDL_FCC_YV12); assert(overlay->planes == 3); int min_height = IJKMIN(out_buffer->height, overlay->h); int dst_y_stride = out_buffer->stride; int dst_c_stride = IJKALIGN(out_buffer->stride / 2, 16); int dst_y_size = dst_y_stride * out_buffer->height; int dst_c_size = dst_c_stride * out_buffer->height / 2; // ALOGE("stride:%d/%d, size:%d/%d", dst_y_stride, dst_c_stride, dst_y_size, dst_c_size); uint8_t *dst_pixels_array[] = { out_buffer->bits, out_buffer->bits + dst_y_size, out_buffer->bits + dst_y_size + dst_c_size, }; int dst_line_height[] = { min_height, min_height / 2, min_height / 2 }; int dst_line_size_array[] = { dst_y_stride, dst_c_stride, dst_c_stride }; for (int i = 0; i < 3; ++i) { int dst_line_size = dst_line_size_array[i]; int src_line_size = overlay->pitches[i]; int line_height = dst_line_height[i]; uint8_t *dst_pixels = dst_pixels_array[i]; const uint8_t *src_pixels = overlay->pixels[i]; if (dst_line_size == src_line_size) { int plane_size = src_line_size * line_height; // ALOGE("sdl_image_copy_plane %p %p %d", dst_pixels, src_pixels, dst_plane_size); memcpy(dst_pixels, src_pixels, plane_size); } else { // TODO: 9 padding int bytewidth = IJKMIN(dst_line_size, src_line_size); // ALOGE("av_image_copy_plane %p %d %p %d %d %d", dst_pixels, dst_line_size, src_pixels, src_line_size, bytewidth, line_height); av_image_copy_plane(dst_pixels, dst_line_size, src_pixels, src_line_size, bytewidth, line_height); } } return 0; }
SDL_VoutOverlay *SDL_VoutFFmpeg_CreateOverlay(int width, int height, Uint32 format, SDL_Vout *display) { SDLTRACE("SDL_VoutFFmpeg_CreateOverlay(w=%d, h=%d, fmt=%.4s(0x%x, dp=%p)", width, height, (const char*) &format, format, display); SDL_VoutOverlay *overlay = SDL_VoutOverlay_CreateInternal(sizeof(SDL_VoutOverlay_Opaque)); if (!overlay) { ALOGE("overlay allocation failed"); return NULL; } SDL_VoutOverlay_Opaque *opaque = overlay->opaque; overlay->format = format; overlay->pitches = opaque->pitches; overlay->pixels = opaque->pixels; overlay->w = width; overlay->h = height; enum AVPixelFormat ff_format = AV_PIX_FMT_NONE; int buf_width = width; // must be aligned to 16 bytes pitch for arm-neon image-convert int buf_height = height; switch (format) { case SDL_FCC_YV12: { ff_format = AV_PIX_FMT_YUV420P; buf_width = IJKALIGN(width, 16); // 1 bytes per pixel for Y-plane opaque->planes = 3; break; } case SDL_FCC_RV16: { ff_format = AV_PIX_FMT_RGB565; buf_width = IJKALIGN(width, 8); // 2 bytes per pixel opaque->planes = 1; break; } case SDL_FCC_RV32: { ff_format = AV_PIX_FMT_0BGR32; buf_width = IJKALIGN(width, 4); // 4 bytes per pixel opaque->planes = 1; break; } default: ALOGE("SDL_VoutFFmpeg_CreateOverlay(...): unknown format %.4s(0x%x)", (char*)&format, format); goto fail; } opaque->frame = alloc_avframe(opaque, ff_format, buf_width, buf_height); if (!opaque->frame) { ALOGE("overlay->opaque->frame allocation failed"); goto fail; } opaque->mutex = SDL_CreateMutex(); overlay_fill(overlay, opaque->frame, opaque->planes); overlay->free_l = overlay_free_l; overlay->lock = overlay_lock; overlay->unlock = overlay_unlock; return overlay; fail: overlay_free_l(overlay); return NULL; }
int sdl_native_window_display_l(ANativeWindow *native_window, SDL_VoutOverlay *overlay) { int retval; if (!native_window) { ALOGE("sdl_native_window_display_l: NULL native_window"); return -1; } if (!overlay) { ALOGE("sdl_native_window_display_l: NULL overlay"); return -1; } if (overlay->w <= 0 || overlay->h <= 0) { ALOGE("sdl_native_window_display_l: invalid overlay dimensions(%d, %d)", overlay->w, overlay->h); return -1; } int curr_w = ANativeWindow_getWidth(native_window); int curr_h = ANativeWindow_getHeight(native_window); int curr_format = ANativeWindow_getFormat(native_window); int buff_w = IJKALIGN(overlay->w, 2); int buff_h = IJKALIGN(overlay->h, 2); AndroidHalFourccDescriptor *voutDesc = native_window_get_desc(curr_format); if (!voutDesc) { ALOGE("sdl_native_window_display_l: unknown hal format: %d", curr_format); return -1; } AndroidHalFourccDescriptor *overlayDesc = native_window_get_desc(overlay->format); if (!overlayDesc) { ALOGE("sdl_native_window_display_l: unknown overlay format: %d", overlay->format); return -1; } if (voutDesc->hal_format != overlayDesc->hal_format) { ALOGD("ANativeWindow_setBuffersGeometry: w=%d, h=%d, f=%.4s(0x%x) => w=%d, h=%d, f=%.4s(0x%x)", curr_w, curr_h, (char*) &curr_format, curr_format, buff_w, buff_h, (char*) &overlay->format, overlay->format); retval = ANativeWindow_setBuffersGeometry(native_window, buff_w, buff_h, overlayDesc->hal_format); if (retval < 0) { ALOGE("sdl_native_window_display_l: ANativeWindow_setBuffersGeometry: failed %d", retval); return retval; } } ANativeWindow_Buffer out_buffer; retval = ANativeWindow_lock(native_window, &out_buffer, NULL); if (retval < 0) { ALOGE("sdl_native_window_display_l: ANativeWindow_lock: failed %d", retval); return retval; } if (out_buffer.width != buff_w || out_buffer.height != buff_h) { ALOGE("unexpected native window buffer (%p)(w:%d, h:%d, fmt:'%.4s'0x%x), expecting (w:%d, h:%d, fmt:'%.4s'0x%x)", native_window, out_buffer.width, out_buffer.height, (char*)&out_buffer.format, out_buffer.format, buff_w, buff_h, (char*)&overlay->format, overlay->format); // TODO: 8 set all black ANativeWindow_unlockAndPost(native_window); ANativeWindow_setBuffersGeometry(native_window, buff_w, buff_h, overlayDesc->hal_format); return -1; } int render_ret = voutDesc->render(&out_buffer, overlay); if (render_ret < 0) { // TODO: 8 set all black // return after unlock image; } retval = ANativeWindow_unlockAndPost(native_window); if (retval < 0) { ALOGE("sdl_native_window_display_l: ANativeWindow_unlockAndPost: failed %d", retval); return retval; } return render_ret; }
SDL_VoutOverlay *SDL_VoutFFmpeg_CreateOverlay(int width, int height, Uint32 format, SDL_Vout *display) { SDLTRACE("SDL_VoutFFmpeg_CreateOverlay(w=%d, h=%d, fmt=%.4s(0x%x, dp=%p)", width, height, (const char*) &format, format, display); SDL_VoutOverlay *overlay = SDL_VoutOverlay_CreateInternal(sizeof(SDL_VoutOverlay_Opaque)); if (!overlay) { ALOGE("overlay allocation failed"); return NULL; } SDL_VoutOverlay_Opaque *opaque = overlay->opaque; overlay->format = format; overlay->pitches = opaque->pitches; overlay->pixels = opaque->pixels; overlay->w = width; overlay->h = height; enum AVPixelFormat ff_format = AV_PIX_FMT_NONE; int buf_width = width; int buf_height = height; switch (format) { case SDL_FCC_I420: case SDL_FCC_YV12: { ff_format = AV_PIX_FMT_YUV420P; // FIXME: need runtime config #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__) // 2^n align for width buf_width = width; if (width > 0) buf_width = 1 << (sizeof(int) * 8 - __builtin_clz(width)); #else buf_width = IJKALIGN(width, 16); // unknown platform #endif opaque->planes = 3; break; } case SDL_FCC_RV16: { ff_format = AV_PIX_FMT_RGB565; buf_width = IJKALIGN(width, 8); // 2 bytes per pixel opaque->planes = 1; break; } 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; } case SDL_FCC_RV32: { ff_format = AV_PIX_FMT_0BGR32; buf_width = IJKALIGN(width, 4); // 4 bytes per pixel opaque->planes = 1; break; } default: ALOGE("SDL_VoutFFmpeg_CreateOverlay(...): unknown format %.4s(0x%x)", (char*)&format, format); goto fail; } opaque->frame = alloc_avframe(opaque, ff_format, buf_width, buf_height); if (!opaque->frame) { ALOGE("overlay->opaque->frame allocation failed"); goto fail; } opaque->mutex = SDL_CreateMutex(); overlay_fill(overlay, opaque->frame, opaque->planes); overlay->free_l = overlay_free_l; overlay->lock = overlay_lock; overlay->unlock = overlay_unlock; return overlay; fail: overlay_free_l(overlay); return NULL; }