Android用surface直接显示yuv数据

原文地址:
http://blog.csdn.net/tung214/article/details/37651825

上一篇文章主要是参照AwesomePlayer直接用SoftwareRenderer类来显示yuv,为了能用到这个类,不惜依赖了libstagefright、libstagefright_color_conversion等动态静态库,从而造成程序具有很高的耦合度,也不便于我们理解yuv数据直接显示的深层次原因。
于是我开始研究SoftwareRenderer的具体实现,我们来提取SoftwareRenderer的核心代码,自己来实现yuv的显示。
SoftwareRenderer就只有三个方法,一个构造函数,一个析构函数,还有一个负责显示的render方法。构造方法里有个很重要的地方native_window_set_buffers_geometry这里是配置即将申请的图形缓冲区的宽高和颜色空间,忽略了这个地方,画面将用默认的值显示,将造成显示不正确。render函数里最重要的三个地方,一个的dequeBuffer,一个是mapper,一个是queue_buffer。

native_window_set_buffers_geometry;//设置宽高以及颜色空间yuv420  native_window_dequeue_buffer_and_wait;//根据以上配置申请图形缓冲区  mapper.lock(buf->handle, GRALLOC_USAGE_SW_WRITE_OFTEN, bounds, &dst));//将申请到的图形缓冲区跨进程映射到用户空间  memcpy(dst, data, dst_y_size + dst_c_size*2);//填充yuv数据到图形缓冲区  mNativeWindow->queueBuffer;//显示  

以上五步是surface显示图形必不可少的五步。
有了以上分析,我们直接上代码:(yuv数据下载地址点击打开链接,放到sdcard)

main.cpp

#include   #include   #include   #include   #include   #include   #include   #include   #include   #include   #include   #include   #include   #include   //ANativeWindow 就是surface,对应surface.cpp里的code  using namespace android;  //将x规整为y的倍数,也就是将x按y对齐  static int ALIGN(int x, int y) {      // y must be a power of 2.      return (x + y - 1) & ~(y - 1);  }  void render(const void *data, size_t size,            const sp &nativeWindow,           int width,int height){      sp mNativeWindow = nativeWindow;      int err;      int mCropWidth = width;      int mCropHeight = height;      int halFormat = HAL_PIXEL_FORMAT_YV12;//颜色空间      int bufWidth = (mCropWidth + 1) & ~1;//按2对齐      int bufHeight = (mCropHeight + 1) & ~1;      CHECK_EQ(0,              native_window_set_usage(              mNativeWindow.get(),              GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN              | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP));      CHECK_EQ(0,              native_window_set_scaling_mode(              mNativeWindow.get(),              NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW));      // Width must be multiple of 32???      //很重要,配置宽高和和指定颜色空间yuv420      //如果这里不配置好,下面deque_buffer只能去申请一个默认宽高的图形缓冲区      CHECK_EQ(0, native_window_set_buffers_geometry(                  mNativeWindow.get(),                  bufWidth,                  bufHeight,                  halFormat));      ANativeWindowBuffer *buf;//描述buffer      //申请一块空闲的图形缓冲区      if ((err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(),              &buf)) != 0) {          ALOGW("Surface::dequeueBuffer returned error %d", err);          return;      }      GraphicBufferMapper &mapper = GraphicBufferMapper::get();      Rect bounds(mCropWidth, mCropHeight);      void *dst;      CHECK_EQ(0, mapper.lock(//用来锁定一个图形缓冲区并将缓冲区映射到用户进程                  buf->handle, GRALLOC_USAGE_SW_WRITE_OFTEN, bounds, &dst));//dst就指向图形缓冲区首地址      if (true){          size_t dst_y_size = buf->stride * buf->height;          size_t dst_c_stride = ALIGN(buf->stride / 2, 16);//1行v/u的大小          size_t dst_c_size = dst_c_stride * buf->height / 2;//u/v的大小          memcpy(dst, data, dst_y_size + dst_c_size*2);//将yuv数据copy到图形缓冲区      }      CHECK_EQ(0, mapper.unlock(buf->handle));      if ((err = mNativeWindow->queueBuffer(mNativeWindow.get(), buf,              -1)) != 0) {          ALOGW("Surface::queueBuffer returned error %d", err);      }      buf = NULL;  }  bool getYV12Data(const char *path,unsigned char * pYUVData,int size){      FILE *fp = fopen(path,"rb");      if(fp == NULL){          printf("read %s fail !!!!!!!!!!!!!!!!!!!\n",path);          return false;      }      fread(pYUVData,size,1,fp);      fclose(fp);      return true;  }  int main(void){      // set up the thread-pool      sp proc(ProcessState::self());      ProcessState::self()->startThreadPool();      // create a client to surfaceflinger      sp client = new SurfaceComposerClient();      sp dtoken(SurfaceComposerClient::getBuiltInDisplay(              ISurfaceComposer::eDisplayIdMain));      DisplayInfo dinfo;      //获取屏幕的宽高等信息      status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);      printf("w=%d,h=%d,xdpi=%f,ydpi=%f,fps=%f,ds=%f\n",           dinfo.w, dinfo.h, dinfo.xdpi, dinfo.ydpi, dinfo.fps, dinfo.density);      if (status)          return -1;      //创建surface      sp surfaceControl = client->createSurface(String8("testsurface"),              dinfo.w, dinfo.h, PIXEL_FORMAT_RGBA_8888, 0);  /*************************get yuv data from file;****************************************/                printf("[%s][%d]\n",__FILE__,__LINE__);      int width,height;      width = 320;      height = 240;      int size = width * height * 3/2;      unsigned char *data = new unsigned char[size];      const char *path = "/mnt/sdcard/yuv_320_240.yuv";      getYV12Data(path,data,size);//get yuv data from file;  /*********************配置surface**********************************************************/      SurfaceComposerClient::openGlobalTransaction();      surfaceControl->setLayer(100000);//设定Z坐标      surfaceControl->setPosition(100, 100);//以左上角为(0,0)设定显示位置      surfaceControl->setSize(width, height);//设定视频显示大小      SurfaceComposerClient::closeGlobalTransaction();      sp surface = surfaceControl->getSurface();      printf("[%s][%d]\n",__FILE__,__LINE__);  /**********************显示yuv数据*************************************************************/         render(data,size,surface,width,height);      printf("[%s][%d]\n",__FILE__,__LINE__);      IPCThreadState::self()->joinThreadPool();//可以保证画面一直显示,否则瞬间消失      IPCThreadState::self()->stopProcess();      return 0;  }  

Android.mk (这次依赖的库少了很多)

LOCAL_PATH:= $(call my-dir)  include $(CLEAR_VARS)  LOCAL_SRC_FILES:= \      main.cpp  LOCAL_SHARED_LIBRARIES := \      libcutils \      libutils \      libbinder \      libui \      libgui \      libstagefright_foundation  LOCAL_MODULE:= MyShowYUV  LOCAL_MODULE_TAGS := tests  include $(BUILD_EXECUTABLE)  

用Java创建UI操作surface来显示yuv数据

下面用Java创建UI并联合JNI层操作surface来直接显示yuv数据(yv12),开发环境为Android 4.4。

package com.example.myyuvviewer;  import java.io.File;  import java.io.FileInputStream;  import android.app.Activity;  import android.os.Bundle;  import android.os.Environment;  import android.util.Log;  import android.view.Surface;  import android.view.SurfaceHolder;  import android.view.SurfaceHolder.Callback;  import android.view.SurfaceView;  public class MainActivity extends Activity {      final private String TAG = "MyYUVViewer";      final private String FILE_NAME = "yuv_320_240.yuv";      private int width = 320;      private int height = 240;      private int size = width * height * 3/2;      @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);          nativeTest();          SurfaceView surfaceview = (SurfaceView) findViewById(R.id.surfaceView);          SurfaceHolder holder = surfaceview.getHolder();          holder.addCallback(new Callback(){              @Override              public void surfaceCreated(SurfaceHolder holder) {                  // TODO Auto-generated method stub                  Log.d(TAG,"surfaceCreated");                  byte[]yuvArray = new byte[size];                  readYUVFile(yuvArray, FILE_NAME);                  nativeSetVideoSurface(holder.getSurface());                  nativeShowYUV(yuvArray,width,height);              }              @Override              public void surfaceChanged(SurfaceHolder holder, int format,                      int width, int height) {                  // TODO Auto-generated method stub              }              @Override              public void surfaceDestroyed(SurfaceHolder holder) {                  // TODO Auto-generated method stub              }});      }      private boolean readYUVFile(byte[] yuvArray,String filename){          try {              // 如果手机插入了SD卡,而且应用程序具有访问SD的权限              if (Environment.getExternalStorageState().equals(                      Environment.MEDIA_MOUNTED)) {                  // 获取SD卡对应的存储目录                  File sdCardDir = Environment.getExternalStorageDirectory();                  // 获取指定文件对应的输入流                  FileInputStream fis = new FileInputStream(                          sdCardDir.getCanonicalPath() +"/" + filename);                  fis.read(yuvArray, 0, size);                  fis.close();                  return true;              } else {                  return false;              }          }catch (Exception e) {              e.printStackTrace();              return false;          }      }      private native void nativeTest();      private native boolean nativeSetVideoSurface(Surface surface);      private native void nativeShowYUV(byte[] yuvArray,int width,int height);      static {          System.loadLibrary("showYUV");      }  }  

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:orientation="vertical" >        <SurfaceView            android:id="@+id/surfaceView"            android:layout_width="fill_parent"            android:layout_height="360dp" />   LinearLayout>  

JNI层,showYUV.cpp(libshowyuv.so)采用动态注册JNI函数的方法.

#include   #include   #include   #include   #include   #include   #include   #include   #include   #include   using namespace android;  static sp surface;  static int ALIGN(int x, int y) {      // y must be a power of 2.      return (x + y - 1) & ~(y - 1);  }  static void render(          const void *data, size_t size, const sp &nativeWindow,int width,int height) {      ALOGE("[%s]%d",__FILE__,__LINE__);      sp mNativeWindow = nativeWindow;      int err;      int mCropWidth = width;      int mCropHeight = height;      int halFormat = HAL_PIXEL_FORMAT_YV12;//颜色空间      int bufWidth = (mCropWidth + 1) & ~1;//按2对齐      int bufHeight = (mCropHeight + 1) & ~1;      CHECK_EQ(0,              native_window_set_usage(              mNativeWindow.get(),              GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN              | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP));      CHECK_EQ(0,              native_window_set_scaling_mode(              mNativeWindow.get(),              NATIVE_WINDOW_SCALING_MODE_SCALE_CROP));      // Width must be multiple of 32???      //很重要,配置宽高和和指定颜色空间yuv420      //如果这里不配置好,下面deque_buffer只能去申请一个默认宽高的图形缓冲区      CHECK_EQ(0, native_window_set_buffers_geometry(                  mNativeWindow.get(),                  bufWidth,                  bufHeight,                  halFormat));      ANativeWindowBuffer *buf;//描述buffer      //申请一块空闲的图形缓冲区      if ((err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(),              &buf)) != 0) {          ALOGW("Surface::dequeueBuffer returned error %d", err);          return;      }      GraphicBufferMapper &mapper = GraphicBufferMapper::get();      Rect bounds(mCropWidth, mCropHeight);      void *dst;      CHECK_EQ(0, mapper.lock(//用来锁定一个图形缓冲区并将缓冲区映射到用户进程                  buf->handle, GRALLOC_USAGE_SW_WRITE_OFTEN, bounds, &dst));//dst就指向图形缓冲区首地址      if (true){          size_t dst_y_size = buf->stride * buf->height;          size_t dst_c_stride = ALIGN(buf->stride / 2, 16);//1行v/u的大小          size_t dst_c_size = dst_c_stride * buf->height / 2;//u/v的大小          memcpy(dst, data, dst_y_size + dst_c_size*2);//将yuv数据copy到图形缓冲区      }      CHECK_EQ(0, mapper.unlock(buf->handle));      if ((err = mNativeWindow->queueBuffer(mNativeWindow.get(), buf,              -1)) != 0) {          ALOGW("Surface::queueBuffer returned error %d", err);      }      buf = NULL;  }  static void nativeTest(){      ALOGE("[%s]%d",__FILE__,__LINE__);  }  static jboolean  nativeSetVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface){      ALOGE("[%s]%d",__FILE__,__LINE__);      surface = android_view_Surface_getSurface(env, jsurface);      if(android::Surface::isValid(surface)){          ALOGE("surface is valid ");      }else {          ALOGE("surface is invalid ");          return false;      }      ALOGE("[%s][%d]\n",__FILE__,__LINE__);      return true;  }  static void  nativeShowYUV(JNIEnv *env, jobject thiz,jbyteArray yuvData,jint width,jint height){      ALOGE("width = %d,height = %d",width,height);      jint len = env->GetArrayLength(yuvData);      ALOGE("len = %d",len);      jbyte *byteBuf = env->GetByteArrayElements(yuvData, 0);      render(byteBuf,len,surface,width,height);  }  static JNINativeMethod gMethods[] = {      {"nativeTest",                  "()V",                              (void *)nativeTest},      {"nativeSetVideoSurface",       "(Landroid/view/Surface;)Z",        (void *)nativeSetVideoSurface},      {"nativeShowYUV",               "([BII)V",                          (void *)nativeShowYUV},  };  static const char* const kClassPathName = "com/example/myyuvviewer/MainActivity";  // This function only registers the native methods  static int register_com_example_myyuvviewer(JNIEnv *env)  {      ALOGE("[%s]%d",__FILE__,__LINE__);      return AndroidRuntime::registerNativeMethods(env,                  kClassPathName, gMethods, NELEM(gMethods));  }  jint JNI_OnLoad(JavaVM* vm, void* reserved)  {      ALOGE("[%s]%d",__FILE__,__LINE__);      JNIEnv* env = NULL;      jint result = -1;      if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {          ALOGE("ERROR: GetEnv failed\n");          goto bail;      }      assert(env != NULL);      ALOGE("[%s]%d",__FILE__,__LINE__);     if (register_com_example_myyuvviewer(env) < 0) {          ALOGE("ERROR: MediaPlayer native registration failed\n");          goto bail;      }      /* success -- return valid version number */      result = JNI_VERSION_1_4;  bail:      return result;  }  

Android.mk

LOCAL_PATH:= $(call my-dir)  include $(CLEAR_VARS)  LOCAL_SRC_FILES:= \      showYUV.cpp  LOCAL_SHARED_LIBRARIES := \      libcutils \      libutils \      libbinder \      libui \      libgui \      libandroid_runtime \      libstagefright_foundation  LOCAL_MODULE:= libshowYUV  LOCAL_MODULE_TAGS := tests  include $(BUILD_SHARED_LIBRARY)  

生成的so文件复制到Java项目里 与src并列的libs/armeabi目录下,没有就手动创建目录,
这样Eclipse会自动把so库打包进apk。

更多相关文章

  1. [Android] ImageView.ScaleType设置图解
  2. Android(安卓)ImageView的scaleType属性与adjustViewBounds属性
  3. Adapter软件设计模式在Android的应用
  4. Android界面布局基本属性
  5. TextView中ellipsize属性
  6. Android(安卓)中的图形图像的渲染
  7. Android(安卓)时间显示控件 TextClock
  8. android显示键盘时的页面自动调整
  9. android开发每日汇总【2011-10-17】

随机推荐

  1. android:allowbackup 属性
  2. Android之底部導航欄--RadioGroup、TabHo
  3. 性能优化(9)-Android ANR在线监控原理
  4. [置顶] Android ListView高度自适应和Scr
  5. Android命令行/c语言/java设置获取系统属
  6. android 自定义menu菜单按键功能
  7. Android应用程序项目结构
  8. android源码下载备注
  9. Android TextWatcher三个回调详解,监听Edi
  10. Android实现在ServiceManager中加入自定