关于havlenapetr-FFMpeg在Android 4.0(ICS)的补充说明

/********************************************************************************************
* 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

这和机器的ROM有关系。因为Android 4.0(ICS)中的libsurfaceflinger.so库没有具体的内容,视频输出有关的surface操作函数由libgui.so完成。而有些SDK对gui库进行改动了。所以把Google官方源码包中的framework/base/libs/gui文件夹和framework/base/include/gui文件夹面的gui库代替机器的SDK中的gui包,重新编译生成ROM,即可正常播放。


附上个人编译修改的播放器:

1、实现了在Android 2.3 和Android 4.0各个平台上面的播放。

2、多格式支持

3、音视频基本同步

4、支持跳转(seek)、暂停、快进、快退、尺寸缩放等等。

5、没开启neon硬件加速时,480P的视频基本流畅播放。





你可能感兴趣的:(关于havlenapetr-FFMpeg在Android 4.0(ICS)的补充说明)