레이블이 android인 게시물을 표시합니다. 모든 게시물 표시
레이블이 android인 게시물을 표시합니다. 모든 게시물 표시

2015년 12월 10일 목요일

java/안드로이드 넌블러킹 소켓을 사용하는 TCP 클라이언트/서버 소스 - java/android nonblocking socket tcp server/client source code

자바/안드로이드 환경에서 TCP 서버는 ServerSocketChannel 클래스를 사용해서 넌블러킹 TCP 서버소켓을 생성후 Selector 를 통해 소켓 select 함수 기능을 수행한다.(소켓 다중화)
TCP 클라이언트는 SocketChannel 클래스를 사용해서 넌블러킹 소켓을 구현한다.

* 서버 종료시 현재 연결된 클라이언트들에 대해 3-way handshake 를 통해 자연스럽게 연결종료 시키는 것에 주목(stopServer 메소드)

< TCPServer.java >
 package com.kimdh.dxmediaplayer;  
   
 import java.net.InetSocketAddress;  
 import java.nio.ByteBuffer;  
 import java.nio.channels.SelectionKey;  
 import java.nio.channels.Selector;  
 import java.nio.channels.ServerSocketChannel;  
 import java.nio.channels.SocketChannel;  
 import java.util.ArrayList;  
 import java.util.Iterator;  
 import java.util.List;  
 import java.util.Set;  
   
 import android.os.StrictMode;  
   
 public class TcpServer {  
      protected ServerSocketChannel mChannel;  
      protected ServerThread mThread;  
        
      private static final int BUFFER_SIZE = 1024 * 1024;  
        
      public boolean isOpened() {  
           if (mChannel == null) return false;  
           return mChannel.isOpen();  
      }       
   
      protected void setNetworkThreadPolicy() {  
           StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();  
           StrictMode.setThreadPolicy(policy);            
      }  
        
      public boolean startServer(int port, ReceiveEventHandler handler) {  
           setNetworkThreadPolicy();  
             
           try {  
                if (mChannel != null) return false;  
                  
                mChannel = ServerSocketChannel.open();  
                mChannel.configureBlocking(false);  
                mChannel.socket().bind(new InetSocketAddress(port));  
                  
                Selector selector = Selector.open();  
                mChannel.register(selector, SelectionKey.OP_ACCEPT);  
                  
                mThread = new ServerThread(mChannel, selector, handler);                                
                mThread.start();                 
                  
                return true;  
           } catch (Exception ex) {  
                ex.printStackTrace();  
                return false;  
           }            
      }  
        
      public void stopServer() {  
           setNetworkThreadPolicy();  
           try {  
                if (mChannel != null) {       
                     mThread.closeAllClient();  
                       
                     while (mThread.getClientCount() > 0)  
                          Thread.sleep(100);  
                       
                     if (mThread != null) {  
                          mThread.mIsRunning = false;  
                          mThread.join();  
                     }            
                     mChannel.close();  
                     mChannel = null;  
                     System.out.println("tcp server channel closed");  
                }  
           } catch (Exception ex) {  
                ex.printStackTrace();  
           }            
      }  
        
      public void closeAllClient() {  
           setNetworkThreadPolicy();  
           if (mChannel != null)       
                mThread.closeAllClient();            
      }  
        
      public boolean send(SocketChannel client, ByteBuffer buffer) {  
           try {  
                if (client.write(buffer) == buffer.limit())   
                     return true;  
           } catch (Exception ex) {  
                ex.printStackTrace();  
           }  
           return false;  
      }       
        
      public interface ReceiveEventHandler {  
           public void onClientConnected(SocketChannel client);  
           public void onReceived(SocketChannel client, ByteBuffer buffer, int len);  
           public void onClientDisconnected(SocketChannel client);  
      }       
        
      protected class ServerThread extends Thread {  
           private ServerSocketChannel mChannel;  
           private Selector mSelector;  
           private List<SocketChannel> mClientList = new ArrayList<SocketChannel>();  
             
           public boolean mIsRunning = false;  
             
           private ReceiveEventHandler mHandler;  
             
           public ServerThread(ServerSocketChannel channel, Selector selector, ReceiveEventHandler handler) {  
                mChannel = channel;  
                mSelector = selector;  
                mIsRunning = true;  
                mHandler = handler;  
           }  
             
           public void closeAllClient() {  
                synchronized (mClientList) {  
                     for (int i=0; i<mClientList.size(); i++) {  
                          try {  
                               mClientList.get(i).socket().shutdownOutput();  
                          } catch (Exception ex) {  
                               ex.printStackTrace();  
                          }  
                     }                      
                }  
           }  
             
           public int getClientCount() {   
                synchronized (mClientList) {  
                     return mClientList.size();                      
                }                 
           }  
             
           @Override  
           public void run() {  
                System.out.println("server thread start");  
                  
                try {  
                     while (mIsRunning) {  
                          mSelector.select(2*1000);  
                            
                          Set keys = mSelector.selectedKeys();  
                          Iterator i = keys.iterator();  
                            
                          while (i.hasNext()) {  
                               SelectionKey key = (SelectionKey)i.next();  
                               i.remove();  
                                 
                               if (key.isAcceptable()) {  
                                    SocketChannel client = mChannel.accept();  
                                    client.configureBlocking(false);  
                                    client.register(mSelector, SelectionKey.OP_READ);  
                                    synchronized (mClientList) {  
                                         mClientList.add(client);                                          
                                    }                                     
                                    System.out.println("client connected : " + client.socket().getRemoteSocketAddress().toString());  
                                    if (mHandler != null) mHandler.onClientConnected(client);  
                                    continue;  
                               }  
                                 
                               if (key.isReadable()) {  
                                    SocketChannel client = (SocketChannel)key.channel();  
                                    ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);  
                                    buffer.clear();  
                                      
                                    int ret;  
                                    try {  
                                         ret = client.read(buffer);  
                                    } catch (Exception ex) {  
                                         ex.printStackTrace();  
                                         key.cancel();  
                                         synchronized (mClientList) {  
                                              mClientList.remove(client);                                               
                                         }                                          
                                         System.out.println("client disconnected : " + client.socket().getRemoteSocketAddress().toString());  
                                         if (mHandler != null) mHandler.onClientDisconnected(client);  
                                         continue;  
                                    }  
                                      
                                    if (ret <= 0) {  
                                         key.cancel();  
                                         synchronized (mClientList) {  
                                              mClientList.remove(client);                                               
                                         }                                                                                       
                                         System.out.println("socket read : " + ret + ", client disconnected : " + client.socket().getRemoteSocketAddress().toString());  
                                         if (mHandler != null) mHandler.onClientDisconnected(client);  
                                         continue;  
                                    }  
                                      
                                    buffer.rewind();  
                                    if (mHandler != null) mHandler.onReceived(client, buffer, ret);  
                               }  
                          }  
                     }  
                } catch (Exception ex) {  
                     ex.printStackTrace();  
                }  
                  
                System.out.println("server thread end");  
           }            
      }  
 }  
   

< TCPClient.java >
 package com.kimdh.dxmediaplayer;  
   
 import java.io.IOException;  
 import java.net.InetSocketAddress;  
 import java.nio.ByteBuffer;  
 import java.nio.channels.SelectionKey;  
 import java.nio.channels.Selector;  
 import java.nio.channels.SocketChannel;  
 import java.util.Iterator;  
 import java.util.Set;  
   
 import android.os.StrictMode;  
   
 public class TcpClient {  
      protected SocketChannel mChannel;  
      protected ReceiveThread mThread;  
        
      public int RECV_BUFFER_SIZE = 1024 * 1024;  
        
      public boolean isConnected() {  
           if (mChannel == null) return false;  
           return mChannel.isConnected();  
      }  
        
      protected void setNetworkThreadPolicy() {  
           StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();  
           StrictMode.setThreadPolicy(policy);            
      }  
             
      public boolean connect(String ipAddress, short port, int timeout, ReceiveEventHandler handler) {  
           setNetworkThreadPolicy();  
                                 
           try     {  
                if (mChannel != null && mChannel.isConnected() == true)  
                     return false;  
                  
                mChannel = SocketChannel.open();  
                mChannel.configureBlocking(false);  
                mChannel.socket().setReceiveBufferSize(RECV_BUFFER_SIZE);  
                  
                mChannel.connect(new InetSocketAddress(ipAddress, port));  
                  
                Selector selector = Selector.open();                 
                SelectionKey clientKey = mChannel.register(selector, SelectionKey.OP_CONNECT);  
                                 
                if (selector.select(timeout*1000) > 0) {  
                     if (clientKey.isConnectable()) {  
                          if (mChannel.finishConnect()) {  
                               mThread = new ReceiveThread(mChannel, handler);  
                               mThread.start();  
                               return true;  
                          }  
                     }                      
                     mChannel.close();  
                     mChannel = null;  
                     return false;                      
                } else {  
                     return false;  
                }  
           } catch (Exception ex) {  
                ex.printStackTrace();  
                return false;  
           }       
      }  
        
      public void close() {  
           setNetworkThreadPolicy();  
           try {  
                if (mChannel != null) {                      
                     if (mThread != null) {  
                          mThread.mIsRunning = false;  
                          mThread.join();  
                     }  
                     mChannel.close();  
                     mChannel = null;  
                     System.out.println("tcp client channel closed");  
                }  
           } catch (Exception ex) {  
                ex.printStackTrace();  
           }  
      }  
             
      public int send(ByteBuffer buffer) {  
           setNetworkThreadPolicy();  
           if (mChannel == null || !mChannel.isConnected()) return -1;            
           try {  
                return mChannel.write(buffer);  
           } catch (IOException ex) {  
                ex.printStackTrace();  
                return -1;  
           }            
      }  
                  
      public interface ReceiveEventHandler {  
           public void onReceived(ByteBuffer buffer, int len);  
           public void onClosed();  
           public void onThreadEvent();  
      }  
             
      protected class ReceiveThread extends Thread {  
           private SocketChannel mChannel;  
           public boolean mIsRunning = false;  
             
           private ReceiveEventHandler mHandler;  
             
           private static final int BUFFER_SIZE = 1024 * 4;  
             
           public ReceiveThread(SocketChannel channel, ReceiveEventHandler handler) {  
                mChannel = channel;                 
                mHandler = handler;  
                mIsRunning = true;  
           }  
             
           @Override  
           public void run() {  
                System.out.println("receive thread start");  
                  
                try {  
                     Selector selector = Selector.open();  
                     mChannel.register(selector, SelectionKey.OP_READ);  
                  
                     while (mIsRunning) {                 
                          if (selector.select(2*1000) > 0) {  
                               Set<SelectionKey> selectedKey = selector.selectedKeys();  
                               Iterator<SelectionKey> iterator = selectedKey.iterator();  
                                 
                               while (iterator.hasNext()) {  
                                    SelectionKey key = iterator.next();  
                                    iterator.remove();  
                                      
                                    if (key.isReadable()) {  
                                         SocketChannel channel = (SocketChannel)key.channel();  
                                         ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);  
                                         buffer.clear();  
                                           
                                         int ret;  
                                         try {  
                                              ret = channel.read(buffer);  
                                         } catch (IOException ex) {  
                                              ex.printStackTrace();  
                                              if (mHandler != null) mHandler.onClosed();  
                                              key.cancel();  
                                              continue;  
                                         }  
                                           
                                         if (ret <= 0) {  
                                              System.out.println("SocketChannel.read returned " + ret);  
                                              if (mHandler != null) mHandler.onClosed();  
                                              key.cancel();  
                                              continue;  
                                         }  
                                           
                                         buffer.rewind();                                                                             
                                                                                   
                                         if (mHandler != null) mHandler.onReceived(buffer, ret);  
                                    }                                     
                               }  
                          }  
                          if (mHandler != null) mHandler.onThreadEvent();  
                     }  
                } catch (Exception ex) {  
                     ex.printStackTrace();  
                }  
                  
                System.out.println("receive thread end");  
           }            
      }  
 }  
   


< TCP 서버 사용 >

public class MainActivity extends Activity implements TcpServer.ReceiveEventHandler {
private TcpServer mServer = new TcpServer();

private byte[] mReceiveBuffer = new byte[1024*4];
private int mReceiveBufferIndex = 0;
        ...
public void onClick(View v) {
        ...
mServer.startServer(8112, this);
    }

protected void onDestroy() {
        ...
mServer.stopServer();
    }

    @Override
public void onClientConnected(SocketChannel client) {
            // do something when client connected

    @Override
public void onReceived(SocketChannel client, ByteBuffer buffer, int len) {
System.out.println("onReceived : " + len);
buffer.get(mReceiveBuffer, mReceiveBufferIndex, len);
mReceiveBufferIndex += len;
// process receive buffer
         ...
         ByteBuffer response = ByteBuffer.allocate(len+4);
         buffer.putInt(len);
         buffer.put(4, payload);
         buffer.rewind();
         mServer.send(client, response);
}

    @Override
public void onClientDisconnected(SocketChannel client) {
// do something when client disconnected
}
}

2015년 11월 25일 수요일

android JNI 를 이용한 c/c++ -> java 메소드 호출 - call method from c/c++ to java with android jni

1. java 에서 호출 메소드 정의
package com.kimdh.dxmediaplayer;
... 
public class DXMediaPlayer {  
   static {  
     System.loadLibrary("DXMediaPlayer");  
   }  
    
   private native void createPlayer();  
   private native void destroyPlayer();  
    
   protected void onDXEvent(int event_type, String strEvent) {  
     ...  
   }  
   ...  
   protected int writeAudioOut(byte[] buffer) {  
     ...  
   }  
 }  

2. java -> c/c++ 함수 호출에서 JavaVM 등 확보
 static JavaVM*  m_pJVM = NULL;  
 jobject    m_object;  
 ...  
 jmethodID    m_method_onDXEvent;  
 ...  
 jmethodID    m_method_writeAudioOut;  
   
 JNIEXPORT void JNICALL Java_com_kimdh_dxmediaplayer_DXMediaPlayer_createPlayer(JNIEnv *env, jobject obj)  
 {  
   m_object = env->NewGlobalRef(obj);  
   
   jclass cls = env->GetObjectClass(m_object);  
   if (cls == NULL) DXPRINTF("Failed to find class\n");  
   
   m_method_onDXEvent = env->GetMethodID(cls, "onDXEvent", "(ILjava/lang/String;)V");  
   if (m_method_onDXEvent == NULL) DXPRINTF("Unable to get method ref : onDXEvent\n");  
   
   m_method_writeAudioOut = env->GetMethodID(cls, "writeAudioOut", "([B)I");
   if (m_method_writeAudioOut == NULL) DXPRINTF("Unable to get method ref : writeAudioOut\n"); 

   if (m_pJVM == NULL)  
     env->GetJavaVM(&m_pJVM);  
 }  
   

3. 호출 함수 정의
 int OnDXEvent(WPARAM wParam, LPARAM lParam)  
 {  
   ...  
   int type = 123;  
   char strEvent[512] = {0};  
   jstring jstrEvent;  
   
   JNIEnv *env;  
    
   int getEnvStat = m_pJVM->GetEnv((void **)&env, JNI_VERSION_1_6);  
   if (getEnvStat == JNI_EDETACHED) {  
     //DXPRINTF("GetEnv: not attached\n");  
     if (m_pJVM->AttachCurrentThread(&env, NULL) != 0) {  
       DXPRINTF("Failed to attach\n");  
       return -1;  
     }  
   } else if (getEnvStat == JNI_OK) {  
     //  
   } else if (getEnvStat == JNI_EVERSION) {  
     DXPRINTF("GetEnv: version not supported\n");  
     return -1;  
   }  
   
   sprintf(strEvent, "event string");  
   
   jstrEvent = env->NewStringUTF(strEvent);  
   
   if (m_method_onDXEvent)  
     env->CallVoidMethod(m_object, m_method_onDXEvent, type, jstrEvent);  
   
   if (env->ExceptionCheck()) {  
     env->ExceptionDescribe();  
   }  
   
   if (getEnvStat == JNI_EDETACHED)  
     m_pJVM->DetachCurrentThread();  
   
   return 0;  
 }  
   
 int WriteAudioOut(unsigned char *buffer, int size)  
 {  
     JNIEnv *env;  
   
     int getEnvStat = m_pJVM->GetEnv((void **)&env, JNI_VERSION_1_6);  
     if (getEnvStat == JNI_EDETACHED) {  
        //DXPRINTF("GetEnv: not attached\n");  
        if (m_pJVM->AttachCurrentThread(&env, NULL) != 0) {  
            DXPRINTF("Failed to attach\n");  
            return -1;  
        }  
     } else if (getEnvStat == JNI_OK) {  
       //  
     } else if (getEnvStat == JNI_EVERSION) {  
        DXPRINTF("GetEnv: version not supported\n");  
        return -1;  
     }  
   
     int ret = -1;  
     if (m_method_writeAudioOut) {  
         jbyteArray jarr = env->NewByteArray(size);  
         jbyte* jbytes = env->GetByteArrayElements(jarr, NULL);  
   
         memcpy(jbytes, buffer, size);  
         env->SetByteArrayRegion(jarr, 0, size, jbytes);  
    
         ret = env->CallIntMethod(m_object, m_method_writeAudioOut, jarr);  
   
         env->ReleaseByteArrayElements(jarr, jbytes, JNI_ABORT);  
     }  
   
     if (env->ExceptionCheck())  
         env->ExceptionDescribe();  
   
     if (getEnvStat == JNI_EDETACHED)  
         m_pJVM->DetachCurrentThread();  
    
     return ret;  
 }  

4. 함수 호출
...
OnDXEvent(NULL, NULL);
...
WriteAudioOut(buf, size);
...

5. JNI 변수 해제
 JNIEXPORT void JNICALL Java_com_kimdh_dxmediaplayer_DXMediaPlayer_destroyPlayer(JNIEnv *env, jobject obj)  
 {  
   env->DeleteGlobalRef(m_object);  
 }  

2015년 4월 23일 목요일

안드로이드 SurfaceView 에 ffmpeg 디코딩 영상 복사하기

1. JNI 를 통해 SurfaceView 의 Surface 객체 받기

public class DXMediaPlayer extends SurfaceView implements SurfaceHolder.Callback {
    ...
    private native void setSurface(int idx, Surface surface);
    ...

@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i(LOG_TAG(), "surface created");
setSurface(mIdx, holder.getSurface());
}
}

ANativeWindow* m_pNativeWindow;
...
JNIEXPORT void JNICALL Java_com_dxmediaplayer_DXMediaPlayer_setSurface(JNIEnv *pEnv, jobject pObj, jint idx, jobject pSurface)
{
if (pSurface != NULL) {
if (!m_pNativeWindow)
m_pNativeWindow = ANativeWindow_fromSurface(pEnv, pSurface);
} else {
if (m_pNativeWindow)
ANativeWindow_release(m_pNativeWindow);
m_pNativeWindow = NULL;
m_nWindowWidth = m_nWindowHeight = 0;
}
}


2. ffmpeg 디코딩 후 Surface 에 복사

ffmpeg 디코딩후 YUV420 -> RGBA 변환(스케일러 사용)후 그대로 복사해주면 된다. 이때 Surface 사이즈에 맞게 스케일링을 할 필요가 없다 (안드로이드에서 스케일링을 해준다)
즉, ffmpeg 스케일러의 sws_getContext 함수에서 소스 사이즈와 데스티네이션 사이즈를 똑같이 AVFrame의 width/height로 해준다(픽셀포멧만 변환)
그리고 아래와같이 Surface에 복사

 ANativeWindow_Buffer windowBuffer;  
   
 if (ANativeWindow_lock(m_pWindow, &windowBuffer, NULL) < 0) {  
    DXPRINTF("ANativeWindow_lock failed\n");  
 } else {  
    if (windowBuffer.width == windowBuffer.stride) {  
       memcpy(windowBuffer.bits, pFrame->data, pFrame->size);  
    } else {  
       int bpp = GetBitPerPixel(pFrame->pix_fmt)>>3;  
       int stride = windowBuffer.stride * bpp;  
       uint8_t *ptr = pFrame->data;  
       uint8_t *dst = (uint8_t*)windowBuffer.bits;  
       for (int h=0; h<pFrame->dst_height; h++) {  
           memcpy(&dst[h*stride], ptr, stride);  
           ptr += pFrame->dst_width * bpp;  
       }  
    }  
 }  
    
 ANativeWindow_unlockAndPost(m_pWindow);  

* opengl es (GLSurfaceView) 를 사용할 필요가 없다.

2015년 1월 26일 월요일

android ffmpeg build

1. make android toolchain
$ /cygdrive/c/android-ndk-r10d/build/tools/make-standalone-toolchain.sh --platform=android-18 --install-dir=c:/my-android-toolchain-18 --arch=arm --system=windows-x86_64

2. ffmpeg configure
./configure --target-os=linux --arch=arm --enable-cross-compile --cc=/cygdrive/c/my-android-toolchain-18/bin/arm-linux-androideabi-gcc --cross-prefix=/cygdrive/c/my-android-toolchain-18/bin/arm-linux-androideabi- --extra-cflags="-marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon" --extra-ldflags="-Wl,--fix-cortex-a8" --disable-doc --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-ffserver --disable-avdevice --disable-network --disable-devices --disable-filters --prefix=../ffmpeg-build

make
make install

// make 후 아래 에러 발생시 config.h 수정
$ make
CC      libavfilter/allfilters.o
In file included from ./libavutil/common.h:83:0,
                 from ./libavutil/avutil.h:289,
                 from libavfilter/avfilter.h:39,
                 from libavfilter/allfilters.c:22:
./config.h:9:18: warning: missing terminating " character [enabled by default]
 (GCC)"e CC_IDENT "gcc 4.8
                  ^
./config.h:10:7: warning: missing terminating " character [enabled by default]
 #define av_restrict restrict
       ^
./config.h:10:2: error: missing terminating " character
 #define av_restrict restrict
  ^
In file included from ./libavutil/intmath.h:30:0,
                 from ./libavutil/common.h:84,
                 from ./libavutil/avutil.h:289,
                 from libavfilter/avfilter.h:39,
                 from libavfilter/allfilters.c:22:
./libavutil/arm/intmath.h:34:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'static'
 static av_always_inline av_const unsigned av_clip_uint8_arm(int a)
 ^
common.mak:49: recipe for target 'libavfilter/allfilters.o' failed
make: *** [libavfilter/allfilters.o] Error 1

config.h 에서
#define CC_IDENT "gcc 4.8
 (GCC)"

#define CC_IDENT "gcc 4.8 (GCC)"
로 수정

3. modify Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := avcodec
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavcodec.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := avformat
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavformat.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := swscale
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libswscale.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := avutil
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libavutil.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := swresample
LOCAL_SRC_FILES := $(LOCAL_PATH)/libs/libswresample.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE    := DXMediaPlayer
LOCAL_SRC_FILES := com_example_dxmediaplayertest_DXMediaPlayer.cpp \
  DXMediaPlayerCtrl.cpp

LOCAL_LDLIBS := -llog -lz -ljnigraphics -landroid

LOCAL_STATIC_LIBRARIES := avformat avcodec avutil swscale swresample    => order must be kept !!!

LOCAL_CFLAGS := -DANDROID -D__STDC_CONSTANT_MACROS
LOCAL_CPPFLAGS := -DANDROID -D__STDC_CONSTANT_MACROS

include $(BUILD_SHARED_LIBRARY)