/********************************************************************************************
* author:conowen@大钟
* E-mail:[email protected]
* http://blog.csdn.net/conowen
* 注:本文为原创,仅作为学习交流使用,转载请标明作者及出处。
********************************************************************************************/
之前的这篇博文http://blog.csdn.net/conowen/article/details/7526398
所提及到的framework/base/native文件夹下面的audio与vedio文件夹可以在https://github.com/havlenapetr/android_frameworks_base
然而,对于Android 4.0(ICS),上述的地址中的ICS分支对于原本的FFmpeg工程显然是不能对接得上的,虽然可以在Android 4.0 的source code中编译通过,但是这个分支的surface.cpp中定义的方法与FFmpeg的JNI方法对应不上。
@Android 4.0 ICS的surface.cpp
/*
* Copyright (C) 2012 Havlena Petr
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "ASurface"
#include
#include
#include
#include
#include
#include
#include "surface.h"
#define CHECK(val) \
if(!val) { \
LOGE("%s [%i]: NULL pointer exception!", __func__, __LINE__); \
return -1; \
}
#define SDK_VERSION_FROYO 8
using namespace android;
typedef struct ASurface {
/* our private members here */
Surface* surface;
SkCanvas canvas;
} ASurface;
static Surface* getNativeSurface(JNIEnv* env, jobject jsurface, int sdkVersion) {
/* we know jsurface is a valid local ref, so use it */
jclass clazz = env->GetObjectClass(jsurface);
if(clazz == NULL) {
LOGE("Can't find surface class!");
return NULL;
}
jfieldID field_surface = env->GetFieldID(clazz,
sdkVersion > SDK_VERSION_FROYO ? "mNativeSurface" : "mSurface",
"I");
if(field_surface == NULL) {
LOGE("Can't find native surface field!");
return NULL;
}
return (Surface *) env->GetIntField(jsurface, field_surface);
}
int ASurface_init(JNIEnv* env, jobject jsurface, int sdkVersion, ASurface** aSurface) {
if(!env || jsurface == NULL) {
LOGE("JNIEnv or jsurface obj is NULL!");
return -1;
}
Surface* surface = getNativeSurface(env, jsurface, sdkVersion);
if(!surface) {
LOGE("Can't obtain native surface!");
return -1;
}
*aSurface = (ASurface *) malloc(sizeof(ASurface));
(*aSurface)->surface = surface;
return 0;
}
void ASurface_deinit(ASurface** aSurface) {
free(*aSurface);
*aSurface = NULL;
}
int ASurface_lock(ASurface* aSurface, AndroidSurfaceInfo* info) {
static Surface::SurfaceInfo surfaceInfo;
CHECK(aSurface);
CHECK(aSurface->surface);
Surface* surface = aSurface->surface;
if (!surface->isValid()) {
LOGE("Native surface isn't valid!");
return -1;
}
int res = surface->lock(&surfaceInfo);
if(res < 0) {
LOGE("Can't lock native surface!");
return res;
}
info->w = surfaceInfo.w;
info->h = surfaceInfo.h;
info->s = surfaceInfo.s;
info->usage = surfaceInfo.usage;
info->format = surfaceInfo.format;
info->bits = surfaceInfo.bits;
return 0;
}
static SkBitmap::Config
convertPixelFormat(APixelFormat format) {
switch(format) {
case ANDROID_PIXEL_FORMAT_RGBX_8888:
case ANDROID_PIXEL_FORMAT_RGBA_8888:
return SkBitmap::kARGB_8888_Config;
case ANDROID_PIXEL_FORMAT_RGB_565:
return SkBitmap::kRGB_565_Config;
}
return SkBitmap::kNo_Config;
}
static void
initBitmap(SkBitmap& bitmap, AndroidSurfaceInfo* info) {
bitmap.setConfig(convertPixelFormat(info->format), info->w, info->h);
if (info->format == ANDROID_PIXEL_FORMAT_RGBX_8888) {
bitmap.setIsOpaque(true);
}
if (info->w > 0 && info->h > 0) {
bitmap.setPixels(info->bits);
} else {
// be safe with an empty bitmap.
bitmap.setPixels(NULL);
}
}
void ASurface_scaleToFullScreen(ASurface* aSurface, AndroidSurfaceInfo* src, AndroidSurfaceInfo* dst) {
SkBitmap srcBitmap;
SkBitmap dstBitmap;
SkMatrix matrix;
initBitmap(srcBitmap, src);
initBitmap(dstBitmap, dst);
matrix.setRectToRect(SkRect::MakeWH(srcBitmap.width(), srcBitmap.height()),
SkRect::MakeWH(dstBitmap.width(), dstBitmap.height()),
SkMatrix::kFill_ScaleToFit);
aSurface->canvas.setBitmapDevice(dstBitmap);
aSurface->canvas.drawBitmapMatrix(srcBitmap, matrix);
}
int ASurface_rotate(ASurface* aSurface, AndroidSurfaceInfo* src, uint32_t degrees) {
SkBitmap bitmap;
CHECK(aSurface);
CHECK(src);
initBitmap(bitmap, src);
aSurface->canvas.setBitmapDevice(bitmap);
return aSurface->canvas.rotate(SkScalar(degrees)) ? 0 : -1;
}
int ASurface_unlockAndPost(ASurface* aSurface) {
CHECK(aSurface);
CHECK(aSurface->surface);
return aSurface->surface->unlockAndPost();
}
@Z:\projects\ffmpeg\jni\libmediaplayer\output.cpp(视频输出接口部分)
//-------------------- Video driver --------------------
//这四个接口都没有在surface.cpp里面定义
int Output::VideoDriver_register(JNIEnv* env, jobject jsurface)
{
return AndroidSurface_register(env, jsurface);
}
int Output::VideoDriver_unregister()
{
return AndroidSurface_unregister();
}
int Output::VideoDriver_getPixels(int width, int height, void** pixels)
{
return AndroidSurface_getPixels(width, height, pixels);
}
int Output::VideoDriver_updateSurface()
{
return AndroidSurface_updateSurface();
}
但是,我们依然可以把Android 2.3 (Gingerbread)中的video模块一样地移植到Android 4.0(ICS)中去,因为这个分支的surface.cpp的方法依然可以和JNI对接上。
编译步骤可以参考之前的博文,这里不再赘述。
但是在ICS的机器上面运行此播放器时,可能会提示以下errors。(Surface::lock failed, already locked)
08-16 21:46:41.863: E/FFMpegMediaPlayer(2263): waiting on video thread
08-16 21:46:41.873: I/FFMpegVideoDecoder(2263): videoclock----> 6.680000
08-16 21:46:41.883: E/SurfaceTextureClient(2263): Surface::lock failed, already locked
08-16 21:46:41.893: I/FFMpegVideoDecoder(2263): videoclock----> 6.720000
08-16 21:46:41.913: E/SurfaceTextureClient(2263): Surface::lock failed, already locked
附上个人编译修改的播放器:
1、实现了在Android 2.3 和Android 4.0各个平台上面的播放。
2、多格式支持
3、音视频基本同步
4、支持跳转(seek)、暂停、快进、快退、尺寸缩放等等。
5、没开启neon硬件加速时,480P的视频基本流畅播放。