- 浏览: 648505 次
- 性别:
- 来自: 广州
文章分类
最新评论
将H.264解码器移植到OPhone操作系统之上(NDK+C),并写一个测试程序(OPhoneSDK+Java)测试解码库是否正常运行,下面是解码时的截图:
OPhone的模拟器和Mobile的模拟器一样是模拟ARM指令的,不像Symbian模拟器一样执行的是本地代码,所以在模拟器上模拟出来的效率会比
真实手机上的效率要低,之前这款解码器已经优化到在nokia 6600(相当低端的一款手机,CPU主频才120Hz)上做到在线播放。
2. 面向人群
本文面向有一定的手机应用开发经验(例如:S60/Mobile/MTK)和有一定的跨手机平台移植经验的人员,帮助她们了解一个企业的核心库(C/C++)是怎么移植到OPhone之上的。
3. 假定前提
1)熟悉Java/C/C++语言;
2)熟悉Java的JNI技术;
3)有一定的跨手机平台移植经验;
4)有一套可供移植的源代码库,这里以H.264解码库为例,为了保护我们的知识版权,这里只能够公开头文件:
- #ifndef__H264DECODE_H__
- #define__H264DECODE_H__
- #ifdefined(__SYMBIAN32__)//S602rd/3rd/UIQ
- #include<e32base.h>
- #include<libc"stdio.h>
- #include<libc"stdlib.h>
- #include<libc"string.h>
- #else//Windows/Mobile/MTK/OPhone
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
- #endif
- classH264Decode
- {
- public:
- /***************************************************************************/
- /*构造解码器*/
- /*@returnH264Decode解码器实例*/
- /***************************************************************************/
- staticH264Decode*H264DecodeConstruct();
- /***************************************************************************/
- /*解码一帧*/
- /*@pInBuffer指向H264的视频流*/
- /*@iInSizeH264视频流的大小*/
- /*@pOutBuffer解码后的视频视频*/
- /*@iOutSize解码后的视频大小*/
- /*@return已解码的H264视频流的尺寸*/
- /***************************************************************************/
- intDecodeOneFrame(unsignedchar*pInBuffer,unsignedintiInSize,unsignedchar*pOutBuffer,unsignedint&iOutSize);
- ~H264Decode();
- };
- #endif//__H264DECODE_H__
#ifndef __H264DECODE_H__ #define __H264DECODE_H__ #if defined(__SYMBIAN32__) //S602rd/3rd/UIQ #include <e32base.h> #include <libc"stdio.h> #include <libc"stdlib.h> #include <libc"string.h> #else //Windows/Mobile/MTK/OPhone #include <stdio.h> #include <stdlib.h> #include <string.h> #endif class H264Decode { public: /***************************************************************************/ /* 构造解码器 */ /* @return H264Decode解码器实例 */ /***************************************************************************/ static H264Decode *H264DecodeConstruct(); /***************************************************************************/ /* 解码一帧 */ /* @pInBuffer 指向H264的视频流 */ /* @iInSize H264视频流的大小 */ /* @pOutBuffer 解码后的视频视频 */ /* @iOutSize 解码后的视频大小 */ /* @return 已解码的H264视频流的尺寸 */ /***************************************************************************/ int DecodeOneFrame(unsigned char *pInBuffer,unsigned int iInSize,unsigned char *pOutBuffer,unsigned int &iOutSize); ~H264Decode(); }; #endif // __H264DECODE_H__
你不用熟悉OPhone平台,一切从零开始,因为在此之前,我也不熟悉。
4. 开发环境(请参考: http://www.ophonesdn.com/documentation/)
5. 移植过程
5.1移植流程
5.2 封装Java接口
在“假定前提”中提到了要移植的函数,接下来会编写这些 函数的Java Native Interface。
- packageophone.streaming.video.h264;
- importjava.nio.ByteBuffer;
- publicclassH264decode{
- //H264解码库指针,因为Java没有指针一说,所以这里用一个32位的数来存放指针的值
- privatelongH264decode=0;
- static{
- System.loadLibrary("H264Decode");
- }
- publicH264decode(){
- this.H264decode=Initialize();
- }
- publicvoidCleanup(){
- Destroy(H264decode);
- }
- publicintDecodeOneFrame(ByteBufferpInBuffer,ByteBufferpOutBuffer){
- returnDecodeOneFrame(H264decode,pInBuffer,pOutBuffer);
- }
- privatenativestaticintDecodeOneFrame(longH264decode,ByteBufferpInBuffer,ByteBufferpOutBuffer);
- privatenativestaticlongInitialize();
- privatenativestaticvoidDestroy(longH264decode);
- }
package ophone.streaming.video.h264; import java.nio.ByteBuffer; public class H264decode { //H264解码库指针,因为Java没有指针一说,所以这里用一个32位的数来存放指针的值 private long H264decode = 0; static{ System.loadLibrary("H264Decode"); } public H264decode() { this.H264decode = Initialize(); } public void Cleanup() { Destroy(H264decode); } public int DecodeOneFrame(ByteBuffer pInBuffer,ByteBuffer pOutBuffer) { return DecodeOneFrame(H264decode, pInBuffer, pOutBuffer); } private native static int DecodeOneFrame(long H264decode,ByteBuffer pInBuffer,ByteBuffer pOutBuffer); private native static long Initialize(); private native static void Destroy(long H264decode); }
这块没什么好说的,就是按照H264解码库的函数,封装的一层接口,如果你熟悉Java JNI,会发现原来是这么类似。这里插入一句:我一直认为技术都是相通的,底层的技术就那么几种,学懂了,其它技术都是一通百通。
5.3 使用C实现本地方法
5.3.1生成头文件
使用javah命令生成JNI头文件,这里需要注意是class路径不是源代码的路径,并且要加上包名:
这里生成了一个ophone_streaming_video_h264_H264decode.h,我们打开来看看:
- #include<jni.h>
- #ifndef_Included_ophone_streaming_video_h264_H264decode
- #define_Included_ophone_streaming_video_h264_H264decode
- #ifdef__cplusplus
- extern"C"{
- #endif
- JNIEXPORTjintJNICALLJava_ophone_streaming_video_h264_H264decode_DecodeOneFrame
- (JNIEnv*,jclass,jlong,jobject,jobject);
- JNIEXPORTjlongJNICALLJava_ophone_streaming_video_h264_H264decode_Initialize
- (JNIEnv*,jclass);
- JNIEXPORTvoidJNICALLJava_ophone_streaming_video_h264_H264decode_Destroy
- (JNIEnv*,jclass,jlong);
- #ifdef__cplusplus
- }
- #endif
- #endif
#include <jni.h> #ifndef _Included_ophone_streaming_video_h264_H264decode #define _Included_ophone_streaming_video_h264_H264decode #ifdef __cplusplus extern "C" { #endif JNIEXPORT jint JNICALL Java_ophone_streaming_video_h264_H264decode_DecodeOneFrame (JNIEnv *, jclass, jlong, jobject, jobject); JNIEXPORT jlong JNICALL Java_ophone_streaming_video_h264_H264decode_Initialize (JNIEnv *, jclass); JNIEXPORT void JNICALL Java_ophone_streaming_video_h264_H264decode_Destroy (JNIEnv *, jclass, jlong); #ifdef __cplusplus } #endif #endif
5.3.2实现本地方法
之前已经生成了JNI头文件,接下来只需要实现这个头文件的几个导出函数,这里以H264解码器的实现为例:
- #include"ophone_streaming_video_h264_H264decode.h"
- #include"H264Decode.h"
- JNIEXPORTjintJNICALLJava_ophone_streaming_video_h264_H264decode_DecodeOneFrame
- (JNIEnv*env,jclassobj,jlongdecode,jobjectpInBuffer,jobjectpOutBuffer){
- H264Decode*pDecode=(H264Decode*)decode;
- unsignedchar*In=NULL;unsignedchar*Out=NULL;
- unsignedintInPosition=0;unsignedintInRemaining=0;unsignedintInSize=0;
- unsignedintOutSize=0;
- jintDecodeSize=-1;
- jbyte*InJbyte=0;
- jbyte*OutJbyte=0;
- jbyteArrayInByteArrary=0;
- jbyteArrayOutByteArrary=0;
- //获取Input/OutByteBuffer相关属性
- {
- //Input
- {
- jclassByteBufferClass=env->GetObjectClass(pInBuffer);
- jmethodIDPositionMethodId=env->GetMethodID(ByteBufferClass,"position","()I");
- jmethodIDRemainingMethodId=env->GetMethodID(ByteBufferClass,"remaining","()I");
- jmethodIDArraryMethodId=env->GetMethodID(ByteBufferClass,"array","()[B");
- InPosition=env->CallIntMethod(pInBuffer,PositionMethodId);
- InRemaining=env->CallIntMethod(pInBuffer,RemainingMethodId);
- InSize=InPosition+InRemaining;
- InByteArrary=(jbyteArray)env->CallObjectMethod(pInBuffer,ArraryMethodId);
- InJbyte=env->GetByteArrayElements(InByteArrary,0);
- In=(unsignedchar*)InJbyte+InPosition;
- }
- //Output
- {
- jclassByteBufferClass=env->GetObjectClass(pOutBuffer);
- jmethodIDArraryMethodId=env->GetMethodID(ByteBufferClass,"array","()[B");
- jmethodIDClearMethodId=env->GetMethodID(ByteBufferClass,"clear","()Ljava/nio/Buffer;");
- //清理输出缓存区
- env->CallObjectMethod(pOutBuffer,ClearMethodId);
- OutByteArrary=(jbyteArray)env->CallObjectMethod(pOutBuffer,ArraryMethodId);
- OutJbyte=env->GetByteArrayElements(OutByteArrary,0);
- Out=(unsignedchar*)OutJbyte;
- }
- }
- //解码
- DecodeSize=pDecode->DecodeOneFrame(In,InRemaining,Out,OutSize);
- //设置Input/OutputByteBuffer相关属性
- {
- //Input
- {
- jclassByteBufferClass=env->GetObjectClass(pInBuffer);
- jmethodIDSetPositionMethodId=env->GetMethodID(ByteBufferClass,"position","(I)Ljava/nio/Buffer;");
- //设置输入缓冲区偏移
- env->CallObjectMethod(pInBuffer,SetPositionMethodId,InPosition+DecodeSize);
- }
- //Output
- {
- jclassByteBufferClass=env->GetObjectClass(pOutBuffer);
- jmethodIDSetPositionMethodId=env->GetMethodID(ByteBufferClass,"position","(I)Ljava/nio/Buffer;");
- //设置输出缓冲区偏移
- env->CallObjectMethod(pOutBuffer,SetPositionMethodId,OutSize);
- }
- }
- //清理
- env->ReleaseByteArrayElements(InByteArrary,InJbyte,0);
- env->ReleaseByteArrayElements(OutByteArrary,OutJbyte,0);
- returnDecodeSize;
- }
- JNIEXPORTjlongJNICALLJava_ophone_streaming_video_h264_H264decode_Initialize
- (JNIEnv*env,jclassobj){
- H264Decode*pDecode=H264Decode::H264DecodeConstruct();
- return(jlong)pDecode;
- }
- JNIEXPORTvoidJNICALLJava_ophone_streaming_video_h264_H264decode_Destroy
- (JNIEnv*env,jclassobj,jlongdecode){
- H264Decode*pDecode=(H264Decode*)decode;
- if(pDecode)
- {
- deletepDecode;
- pDecode=NULL;
- }
- }
#include "ophone_streaming_video_h264_H264decode.h" #include "H264Decode.h" JNIEXPORT jint JNICALL Java_ophone_streaming_video_h264_H264decode_DecodeOneFrame (JNIEnv * env, jclass obj, jlong decode, jobject pInBuffer, jobject pOutBuffer) { H264Decode *pDecode = (H264Decode *)decode; unsigned char *In = NULL;unsigned char *Out = NULL; unsigned int InPosition = 0;unsigned int InRemaining = 0;unsigned int InSize = 0; unsigned int OutSize = 0; jint DecodeSize = -1; jbyte *InJbyte = 0; jbyte *OutJbyte = 0; jbyteArray InByteArrary = 0; jbyteArray OutByteArrary = 0; //获取Input/Out ByteBuffer相关属性 { //Input { jclass ByteBufferClass = env->GetObjectClass(pInBuffer); jmethodID PositionMethodId = env->GetMethodID(ByteBufferClass,"position","()I"); jmethodID RemainingMethodId = env->GetMethodID(ByteBufferClass,"remaining","()I"); jmethodID ArraryMethodId = env->GetMethodID(ByteBufferClass,"array","()[B"); InPosition = env->CallIntMethod(pInBuffer,PositionMethodId); InRemaining = env->CallIntMethod(pInBuffer,RemainingMethodId); InSize = InPosition + InRemaining; InByteArrary = (jbyteArray)env->CallObjectMethod(pInBuffer,ArraryMethodId); InJbyte = env->GetByteArrayElements(InByteArrary,0); In = (unsigned char*)InJbyte + InPosition; } //Output { jclass ByteBufferClass = env->GetObjectClass(pOutBuffer); jmethodID ArraryMethodId = env->GetMethodID(ByteBufferClass,"array","()[B"); jmethodID ClearMethodId = env->GetMethodID(ByteBufferClass,"clear","()Ljava/nio/Buffer;"); //清理输出缓存区 env->CallObjectMethod(pOutBuffer,ClearMethodId); OutByteArrary = (jbyteArray)env->CallObjectMethod(pOutBuffer,ArraryMethodId); OutJbyte = env->GetByteArrayElements(OutByteArrary,0); Out = (unsigned char*)OutJbyte; } } //解码 DecodeSize = pDecode->DecodeOneFrame(In,InRemaining,Out,OutSize); //设置Input/Output ByteBuffer相关属性 { //Input { jclass ByteBufferClass = env->GetObjectClass(pInBuffer); jmethodID SetPositionMethodId = env->GetMethodID(ByteBufferClass,"position","(I)Ljava/nio/Buffer;"); //设置输入缓冲区偏移 env->CallObjectMethod(pInBuffer,SetPositionMethodId,InPosition + DecodeSize); } //Output { jclass ByteBufferClass = env->GetObjectClass(pOutBuffer); jmethodID SetPositionMethodId = env->GetMethodID(ByteBufferClass,"position","(I)Ljava/nio/Buffer;"); //设置输出缓冲区偏移 env->CallObjectMethod(pOutBuffer,SetPositionMethodId,OutSize); } } //清理 env->ReleaseByteArrayElements(InByteArrary,InJbyte,0); env->ReleaseByteArrayElements(OutByteArrary,OutJbyte,0); return DecodeSize; } JNIEXPORT jlong JNICALL Java_ophone_streaming_video_h264_H264decode_Initialize (JNIEnv * env, jclass obj) { H264Decode *pDecode = H264Decode::H264DecodeConstruct(); return (jlong)pDecode; } JNIEXPORT void JNICALL Java_ophone_streaming_video_h264_H264decode_Destroy (JNIEnv * env, jclass obj, jlong decode) { H264Decode *pDecode = (H264Decode *)decode; if (pDecode) { delete pDecode; pDecode = NULL; } }
5.3.3编译本地方法
接下来,只需要把用C实现的本地方法编译为动态链接库,如果之前你用于移植的那个库曾经移植到Symbian上过,那么编译会相当简单,因为NDK的编译器和Symbian的编译器一样,都是采用GCC做交叉编译器。
首先,需要在$NDK"apps目录下,创建一个项目目录,这里创建了一个H264Decode目录,在H264Decode目录中,创建一个Android.mk文件:
- APP_PROJECT_PATH:=$(callmy-dir)
- APP_MODULES:=H264Decode
APP_PROJECT_PATH := $(call my-dir) APP_MODULES := H264Decode
接下来,需要在$NDK"source目录下,创建源代码目录(这里的目录名要和上面创建的项目目录文件名相同),这里创建一个H264Decode目录,然后把之前生成的JNI头文件和你实现的本地方法相关头文件和源代码,都拷贝到 这个目录下面。
然后,我们编辑Android.mk文件:
- LOCAL_PATH:=$(callmy-dir)
- include$(CLEAR_VARS)
- LOCAL_MODULE:=H264Decode
- LOCAL_SRC_FILES:=common.ccabac.cutils.cgolomb.cmpegvideo.cmem.cimgconvert.ch264decode.cpph264.cdsputil.cophone_streaming_video_h264_H264decode.cpp
- include$(BUILD_SHARED_LIBRARY)
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := H264Decode LOCAL_SRC_FILES := common.c cabac.c utils.c golomb.c mpegvideo.c mem.c imgconvert.c h264decode.cpp h264.c dsputil.c ophone_streaming_video_h264_H264decode.cpp include $(BUILD_SHARED_LIBRARY)
关于Android.mk文件中,各个字段的解释,可以参考$NDK"doc下的《OPHONE-MK.TXT》和《OVERVIEW.TXT》,里面有详细的介绍。
最后,我们启动Cygwin,开始编译:
如果你看到了Install:**,这说明你的库已经编译好了。
FAQ 2:
如果编译遇到下面错误,怎么办?
- error:redefinitionoftypedef'int8_t'
需要注释掉你的代码中“typedef signed char int8_t;”,如果你的代码之前是已经移植到了Mobile/Symbian上的话,很有可能遇到这个问题。
5.4编写库测试程序
用Eclipse创建一个OPhone工程,在入口类中输入如下代码:
- /**
- *@authorophone
- *@email3751624@qq.com
- */
- packageophone.streaming.video.h264;
- importjava.io.File;
- importjava.io.FileInputStream;
- importjava.io.InputStream;
- importjava.nio.ByteBuffer;
- importOPhone.app.Activity;
- importOPhone.graphics.BitmapFactory;
- importOPhone.os.Bundle;
- importOPhone.os.Handler;
- importOPhone.os.Message;
- importOPhone.widget.ImageView;
- importOPhone.widget.TextView;
- publicclassH264ExampleextendsActivity{
- privatestaticfinalintVideoWidth=352;
- privatestaticfinalintVideoHeight=288;
- privateImageViewImageLayout=null;
- privateTextViewFPSLayout=null;
- privateH264decodeDecode=null;
- privateHandlerH=null;
- privatebyte[]Buffer=null;
- privateintDecodeCount=0;
- privatelongStartTime=0;
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- ImageLayout=(ImageView)findViewById(R.id.ImageView);
- FPSLayout=(TextView)findViewById(R.id.TextView);
- Decode=newH264decode();
- StartTime=System.currentTimeMillis();
- newThread(newRunnable(){
- publicvoidrun(){
- StartDecode();
- }
- }).start();
- H=newHandler(){
- publicvoidhandleMessage(Messagemsg){
- ImageLayout.invalidate();
- ImageLayout.setImageBitmap(BitmapFactory.decodeByteArray(Buffer,0,Buffer.length));
- longTime=(System.currentTimeMillis()-StartTime)/1000;
- if(Time>0){
- FPSLayout.setText("花费时间:"+Time+"秒解码帧数:"+DecodeCount+"FPS:"+(DecodeCount/Time));
- }
- }
- };
- }
- privatevoidStartDecode(){
- Fileh264file=newFile("/tmp/Demo.264");
- InputStreamh264stream=null;
- try{
- h264stream=newFileInputStream(h264file);
- ByteBufferpInBuffer=ByteBuffer.allocate(51200);//分配50k的缓存
- ByteBufferpRGBBuffer=ByteBuffer.allocate(VideoWidth*VideoHeight*3);
- while(h264stream.read(pInBuffer.array(),pInBuffer.position(),pInBuffer.remaining())>=0){
- pInBuffer.position(0);
- do{
- intDecodeLength=Decode.DecodeOneFrame(pInBuffer,pRGBBuffer);
- //如果解码成功,把解码出来的图片显示出来
- if(DecodeLength>0&&pRGBBuffer.position()>0){
- //转换RGB字节为BMP
- BMPImagebmp=newBMPImage(pRGBBuffer.array(),VideoWidth,VideoHeight);
- Buffer=bmp.getByte();
- H.sendMessage(H.obtainMessage());
- Thread.sleep(1);
- DecodeCount++;
- }
- }while(pInBuffer.remaining()>10240);//确保缓存区里面的数据始终大于10k
- //清理已解码缓冲区
- intRemaining=pInBuffer.remaining();
- System.arraycopy(pInBuffer.array(),pInBuffer.position(),pInBuffer.array(),0,Remaining);
- pInBuffer.position(Remaining);
- }
- }catch(Exceptione1){
- e1.printStackTrace();
- }finally{
- try{h264stream.close();}catch(Exceptione){}
- }
- }
- protectedvoidonDestroy(){
- super.onDestroy();
- Decode.Cleanup();
- }
- }
/** * @author ophone * @email 3751624@qq.com */ package ophone.streaming.video.h264; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.nio.ByteBuffer; import OPhone.app.Activity; import OPhone.graphics.BitmapFactory; import OPhone.os.Bundle; import OPhone.os.Handler; import OPhone.os.Message; import OPhone.widget.ImageView; import OPhone.widget.TextView; public class H264Example extends Activity { private static final int VideoWidth = 352; private static final int VideoHeight = 288; private ImageView ImageLayout = null; private TextView FPSLayout = null; private H264decode Decode = null; private Handler H = null; private byte[] Buffer = null; private int DecodeCount = 0; private long StartTime = 0; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ImageLayout = (ImageView) findViewById(R.id.ImageView); FPSLayout = (TextView) findViewById(R.id.TextView); Decode = new H264decode(); StartTime = System.currentTimeMillis(); new Thread(new Runnable(){ public void run() { StartDecode(); } }).start(); H = new Handler(){ public void handleMessage(Message msg) { ImageLayout.invalidate(); ImageLayout.setImageBitmap(BitmapFactory.decodeByteArray(Buffer, 0, Buffer.length)); long Time = (System.currentTimeMillis()-StartTime)/1000; if(Time > 0){ FPSLayout.setText("花费时间:" + Time + "秒 解码帧数:" + DecodeCount + " FPS:" + (DecodeCount/Time) ); } } }; } private void StartDecode(){ File h264file = new File("/tmp/Demo.264"); InputStream h264stream = null; try { h264stream = new FileInputStream(h264file); ByteBuffer pInBuffer = ByteBuffer.allocate(51200);//分配50k的缓存 ByteBuffer pRGBBuffer = ByteBuffer.allocate(VideoWidth*VideoHeight*3); while (h264stream.read(pInBuffer.array(), pInBuffer.position(), pInBuffer.remaining()) >= 0) { pInBuffer.position(0); do{ int DecodeLength = Decode.DecodeOneFrame(pInBuffer, pRGBBuffer); //如果解码成功,把解码出来的图片显示出来 if(DecodeLength > 0 && pRGBBuffer.position() > 0){ //转换RGB字节为BMP BMPImage bmp = new BMPImage(pRGBBuffer.array(),VideoWidth,VideoHeight); Buffer = bmp.getByte(); H.sendMessage(H.obtainMessage()); Thread.sleep(1); DecodeCount ++; } }while(pInBuffer.remaining() > 10240);//确保缓存区里面的数据始终大于10k //清理已解码缓冲区 int Remaining = pInBuffer.remaining(); System.arraycopy(pInBuffer.array(), pInBuffer.position(), pInBuffer.array(), 0, Remaining); pInBuffer.position(Remaining); } } catch (Exception e1) { e1.printStackTrace(); } finally { try{h264stream.close();} catch(Exception e){} } } protected void onDestroy() { super.onDestroy(); Decode.Cleanup(); } }
BMPImage是一个工具类,主要用于把RGB序列,转换为BMP图象用于显示:
- @authorophone
- *@email3751624@qq.com
- */
- packageophone.streaming.video.h264;
- importjava.nio.ByteBuffer;
- publicclassBMPImage{
- //---私有常量
- privatefinalstaticintBITMAPFILEHEADER_SIZE=14;
- privatefinalstaticintBITMAPINFOHEADER_SIZE=40;
- //---位图文件标头
- privatebytebfType[]={'B','M'};
- privateintbfSize=0;
- privateintbfReserved1=0;
- privateintbfReserved2=0;
- privateintbfOffBits=BITMAPFILEHEADER_SIZE+BITMAPINFOHEADER_SIZE;
- //---位图信息标头
- privateintbiSize=BITMAPINFOHEADER_SIZE;
- privateintbiWidth=176;
- privateintbiHeight=144;
- privateintbiPlanes=1;
- privateintbiBitCount=24;
- privateintbiCompression=0;
- privateintbiSizeImage=biWidth*biHeight*3;
- privateintbiXPelsPerMeter=0x0;
- privateintbiYPelsPerMeter=0x0;
- privateintbiClrUsed=0;
- privateintbiClrImportant=0;
- ByteBufferbmpBuffer=null;
- publicBMPImage(byte[]Data,intWidth,intHeight){
- biWidth=Width;
- biHeight=Height;
- biSizeImage=biWidth*biHeight*3;
- bfSize=BITMAPFILEHEADER_SIZE+BITMAPINFOHEADER_SIZE+biWidth*biHeight*3;
- bmpBuffer=ByteBuffer.allocate(BITMAPFILEHEADER_SIZE+BITMAPINFOHEADER_SIZE+biWidth*biHeight*3);
- writeBitmapFileHeader();
- writeBitmapInfoHeader();
- bmpBuffer.put(Data);
- }
- publicbyte[]getByte(){
- returnbmpBuffer.array();
- }
- privatebyte[]intToWord(intparValue){
- byteretValue[]=newbyte[2];
- retValue[0]=(byte)(parValue&0x00FF);
- retValue[1]=(byte)((parValue>>8)&0x00FF);
- return(retValue);
- }
- privatebyte[]intToDWord(intparValue){
- byteretValue[]=newbyte[4];
- retValue[0]=(byte)(parValue&0x00FF);
- retValue[1]=(byte)((parValue>>8)&0x000000FF);
- retValue[2]=(byte)((parValue>>16)&0x000000FF);
- retValue[3]=(byte)((parValue>>24)&0x000000FF);
- return(retValue);
- }
- privatevoidwriteBitmapFileHeader(){
- bmpBuffer.put(bfType);
- bmpBuffer.put(intToDWord(bfSize));
- bmpBuffer.put(intToWord(bfReserved1));
- bmpBuffer.put(intToWord(bfReserved2));
- bmpBuffer.put(intToDWord(bfOffBits));
- }
- privatevoidwriteBitmapInfoHeader(){
- bmpBuffer.put(intToDWord(biSize));
- bmpBuffer.put(intToDWord(biWidth));
- bmpBuffer.put(intToDWord(biHeight));
- bmpBuffer.put(intToWord(biPlanes));
- bmpBuffer.put(intToWord(biBitCount));
- bmpBuffer.put(intToDWord(biCompression));
- bmpBuffer.put(intToDWord(biSizeImage));
- bmpBuffer.put(intToDWord(biXPelsPerMeter));
- bmpBuffer.put(intToDWord(biYPelsPerMeter));
- bmpBuffer.put(intToDWord(biClrUsed));
- bmpBuffer.put(intToDWord(biClrImportant));
- }
- }
@author ophone * @email 3751624@qq.com */ package ophone.streaming.video.h264; import java.nio.ByteBuffer; public class BMPImage { // --- 私有常量 private final static int BITMAPFILEHEADER_SIZE = 14; private final static int BITMAPINFOHEADER_SIZE = 40; // --- 位图文件标头 private byte bfType[] = { 'B', 'M' }; private int bfSize = 0; private int bfReserved1 = 0; private int bfReserved2 = 0; private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; // --- 位图信息标头 private int biSize = BITMAPINFOHEADER_SIZE; private int biWidth = 176; private int biHeight = 144; private int biPlanes = 1; private int biBitCount = 24; private int biCompression = 0; private int biSizeImage = biWidth*biHeight*3; private int biXPelsPerMeter = 0x0; private int biYPelsPerMeter = 0x0; private int biClrUsed = 0; private int biClrImportant = 0; ByteBuffer bmpBuffer = null; public BMPImage(byte[] Data,int Width,int Height){ biWidth = Width; biHeight = Height; biSizeImage = biWidth*biHeight*3; bfSize = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE + biWidth*biHeight*3; bmpBuffer = ByteBuffer.allocate(BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE + biWidth*biHeight*3); writeBitmapFileHeader(); writeBitmapInfoHeader(); bmpBuffer.put(Data); } public byte[] getByte(){ return bmpBuffer.array(); } private byte[] intToWord(int parValue) { byte retValue[] = new byte[2]; retValue[0] = (byte) (parValue & 0x00FF); retValue[1] = (byte) ((parValue >> 8) & 0x00FF); return (retValue); } private byte[] intToDWord(int parValue) { byte retValue[] = new byte[4]; retValue[0] = (byte) (parValue & 0x00FF); retValue[1] = (byte) ((parValue >> 8) & 0x000000FF); retValue[2] = (byte) ((parValue >> 16) & 0x000000FF); retValue[3] = (byte) ((parValue >> 24) & 0x000000FF); return (retValue); } private void writeBitmapFileHeader () { bmpBuffer.put(bfType); bmpBuffer.put(intToDWord (bfSize)); bmpBuffer.put(intToWord (bfReserved1)); bmpBuffer.put(intToWord (bfReserved2)); bmpBuffer.put(intToDWord (bfOffBits)); } private void writeBitmapInfoHeader () { bmpBuffer.put(intToDWord (biSize)); bmpBuffer.put(intToDWord (biWidth)); bmpBuffer.put(intToDWord (biHeight)); bmpBuffer.put(intToWord (biPlanes)); bmpBuffer.put(intToWord (biBitCount)); bmpBuffer.put(intToDWord (biCompression)); bmpBuffer.put(intToDWord (biSizeImage)); bmpBuffer.put(intToDWord (biXPelsPerMeter)); bmpBuffer.put(intToDWord (biYPelsPerMeter)); bmpBuffer.put(intToDWord (biClrUsed)); bmpBuffer.put(intToDWord (biClrImportant)); } }
测试程序完整工程在此暂不提供。
5.5集成测试
集成测试有两点需要注意,在运行程序前,需要把动态库复制到模拟器的/system/lib目录下面,还需要把需要解码的视频传到模拟器的/tmp目录下。
这里要明确的是,OPhone和Symbian的模拟器都做的太不人性化了,Symbian复制一个文件到模拟器中,要进一堆很深的目录,OPhone的
更恼火,需要敲命令把文件传递到模拟器里,说实话,仅在这点上,Mobile的模拟器做的还是非常人性化的。
命令:
- PATH=D:"OPhone"OPhoneSDK"tools"
- adb.exeremount
- adb.exepushD:"Eclipse"workspace"H264Example"libs"armeabi"libH264Decode.so/system/lib
- adb.exepushD:"Eclipse"workspace"H264Example"Demo.264/tmp
- pause
PATH=D:"OPhone"OPhone SDK"tools" adb.exe remount adb.exe push D:"Eclipse"workspace"H264Example"libs"armeabi"libH264Decode.so /system/lib adb.exe push D:"Eclipse"workspace"H264Example"Demo.264 /tmp pause
这里解释一下abd push命令:
adb push <本地文件路径> <远程文件路径> - 复制文件或者目录到模拟器
在Eclipse中,启动库测试程序,得到画面如下:
FAQ 3:
模拟器黑屏怎么办?
这可能是由于模拟器启动速度比较慢所引起的,所以需要多等一会。希望下个版本能够改进。
原文地址:http://www.ophonesdn.com/article/show/45;jsessionid=306BD3BE92F43DC693BEB09B0234B036
国内最棒的Google Android技术社区(eoeandroid),欢迎访问!
《银河系列原创教程》发布
《Java Web开发速学宝典》出版,欢迎定购
相关推荐
OPhone UI开发者指南 OPhone联网应用开发中的线程管理与界面更新 OPhone平台开发环境常见问题 OPhone游戏编程 流媒体程序开发之:H264解码器移植到OPhone 如何在OPhone平台编写网络应用
OPhone平台多媒体应用程序开发主要涉及Android系统的多媒体架构、多媒体驱动、OpenCore库、多媒体框架层以及关键的Java API。在Android系统中,多媒体组件自下而上包括多媒体驱动、多媒体Native库、多媒体框架层和...
### Android OPhone 开发完全讲义 #### 一、概览与目标读者 《Android OPhone 开发完全讲义》是一本集成了Android与OPhone两大移动操作系统开发知识的著作,由国内著名Android社区eoeandroid推荐。本书旨在为具备...
《Android/Ophone开发完全讲义》是李宁老师编著的一本深入讲解Android与Ophone开发的书籍,全面覆盖了这两个平台的基础知识和技术要点。在本讲义中,作者旨在帮助开发者从零基础开始,逐步掌握Android和Ophone应用...
手机游戏是目前深受广大手机用户喜爱的一种娱乐软件,各大运营商都有自己的手机游戏平台,比如中国移动的百宝箱,MM平台,当然...有了这个优点也就意味着所有的J2ME程序都可以非常快速的迁移到OPhone(Adnroid)平台。
在本文中,我们将深入探讨如何配置Ophone开发环境并创建一个简单的Hello,OPhone应用程序,以便测试Ophone SDK的正确安装和使用。Ophone是一种基于Linux的智能手机操作系统,由中国移动开发,它支持Android应用程序...
7. **多媒体支持**:OPhone可能对多媒体文件的处理有特别的需求,例如音频、视频的编码解码和流媒体播放。 8. **OPhone特定服务**:如位置服务、推送通知、运营商API等,这些都是OPhone独有的服务,开发者需要掌握...
- **OPhone SDK 组成**:OPhone SDK 包含了用于开发应用程序的所有必要工具和资源,包括 API、模拟器、开发工具以及示例代码。 - **OPhone API**:提供了一系列特定于 OPhone 的功能,例如主屏幕管理、本地搜索、...
本书系统地介绍了OPhone平台的体系结构、应用程序开发流程和调试技巧、OPhone应用程序开发中涉及的主要模块。全书结合30多个经典案例,阐述了OPhone平台的运行环境、应用程序模型、用户界面与图形引擎、数据持久化...
这本书旨在帮助开发者全面掌握Android和OPhone平台的开发技能,从基础到高级,覆盖了整个移动应用开发的生命周期。 在Android开发部分,该讲义首先介绍了Android平台的基础知识,包括Android系统架构、开发环境搭建...
《Android/OPhone开发完全讲义》是一本深入探讨Android和OPhone开发的综合性教材,主要针对想要在Android平台上进行应用程序开发的工程师和技术爱好者。这本书不仅涵盖了Android的基础知识,还涉及了OPhone这一中国...
《Android/OPhone开发完全讲义》是李宁编著的一本专为Android和OPhone开发者准备的详尽教程,其上册主要涵盖了Android和OPhone平台的基础知识、开发环境搭建以及应用程序的基本构建过程。这份讲义对于初学者和有一定...
OPhone是中国移动主导的一款基于Android操作系统定制的开放式手机平台,它允许开发者利用Java或C++进行应用程序的开发。本文将深入探讨OPhone开发环境的搭建,帮助初学者快速上手,避免常见问题。 首先,我们需要...
《Android-OPhone开发完全讲义》是一本深入探讨Android应用程序开发的专业书籍,特别是针对OPhone平台的开发。OPhone是基于Android系统的一种定制版本,由中国移动推出,它在原生Android的基础上添加了一些特定的...
《Android OPhone开发完全讲义源码Android-OPhone-src》是一份全面涵盖Android OPhone开发的源码解析资料,包含多个章节的源代码实例,旨在帮助开发者深入了解Android OPhone平台的内部工作原理和开发技术。...
《Android/Ophone开发完全讲义》是一份深入探讨Android与Ophone平台开发的综合性学习资料,其中包含了丰富的源码示例,旨在帮助开发者全面掌握这两种操作系统上的应用开发技能。源码是学习编程语言和理解软件架构的...
OPhone SDK,全称为Open Mobile Phone SDK,是基于Android SDK的扩展,主要用于开发针对OPhone操作系统的应用程序。OPhone是中国移动推出的一个基于Linux的智能手机操作系统,它融合了Android开放源码项目的优势,为...
《Ophone应用开发权威指南》是一本专注于Ophone平台应用程序开发的专业书籍,其附带的光盘源码为读者提供了丰富的实践素材。Ophone是中国移动基于Android操作系统进行深度定制的一个智能手机平台,它在保留Android...