<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 312, 文章 - 14, 評論 - 1393, 引用 - 0
    數據加載中……

    流媒體程序開發之:H264解碼器移植到OPhone

    1.   移植目標
           將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解碼庫為例,為了保護我們的知識版權,這里只能夠公開頭文件:
      

    1. #ifndef __H264DECODE_H__  
    2. #define __H264DECODE_H__  
    3.   
    4. #if defined(__SYMBIAN32__)  //S602rd/3rd/UIQ  
    5.     #include <e32base.h>  
    6.     #include <libc"stdio.h>  
    7.     #include <libc"stdlib.h>  
    8.     #include <libc"string.h>  
    9. #else                       //Windows/Mobile/MTK/OPhone  
    10.     #include <stdio.h>  
    11.     #include <stdlib.h>  
    12.     #include <string.h>  
    13. #endif  
    14.   
    15. class H264Decode  
    16. {  
    17. public:  
    18.     /***************************************************************************/  
    19.     /* 構造解碼器                                                        */  
    20.     /* @return H264Decode解碼器實例                                      */  
    21.     /***************************************************************************/  
    22.     static H264Decode *H264DecodeConstruct();  
    23.     /***************************************************************************/  
    24.     /* 解碼一幀                                                     */  
    25.     /* @pInBuffer   指向H264的視頻流                                      */  
    26.     /* @iInSize H264視頻流的大小                                      */  
    27.     /* @pOutBuffer  解碼后的視頻視頻                                        */  
    28.     /* @iOutSize    解碼后的視頻大小                                        */  
    29.     /* @return      已解碼的H264視頻流的尺寸                              */  
    30.     /***************************************************************************/  
    31.     int DecodeOneFrame(unsigned char *pInBuffer,unsigned int iInSize,unsigned char *pOutBuffer,unsigned int &iOutSize);  
    32.     ~H264Decode();  
    33. };  
    34. #endif  // __H264DECODE_H__  

          你不用熟悉OPhone平臺,一切從零開始,因為在此之前,我也不熟悉。
     

    4.  開發環境(請參考: http://www.ophonesdn.com/documentation/)
     

    5.  移植過程

    5.1 移植流程

    5.2 封裝Java接口

            在“假定前提”中提到了要移植的函數,接下來會編寫這些 函數的Java Native Interface。

    1. package ophone.streaming.video.h264;  
    2.   
    3. import java.nio.ByteBuffer;  
    4.   
    5. public class H264decode {  
    6.       
    7. //H264解碼庫指針,因為Java沒有指針一說,所以這里用一個32位的數來存放指針的值  
    8.     private long H264decode = 0;  
    9.       
    10.     static{     
    11.         System.loadLibrary("H264Decode");  
    12.     }  
    13.   
    14.     public H264decode() {  
    15.         this.H264decode = Initialize();  
    16.     }  
    17.       
    18.     public void Cleanup() {  
    19.         Destroy(H264decode);  
    20.     }  
    21.       
    22.     public int DecodeOneFrame(ByteBuffer pInBuffer,ByteBuffer pOutBuffer) {  
    23.         return DecodeOneFrame(H264decode, pInBuffer, pOutBuffer);  
    24.     }  
    25.   
    26.     private native static int DecodeOneFrame(long H264decode,ByteBuffer pInBuffer,ByteBuffer pOutBuffer);  
    27.     private native static long Initialize();  
    28.     private native static void Destroy(long H264decode);  
    29. }  


             這塊沒什么好說的,就是按照H264解碼庫的函數,封裝的一層接口,如果你熟悉Java JNI,會發現原來是這么類似。這里插入一句:我一直認為技術都是相通的,底層的技術就那么幾種,學懂了,其它技術都是一通百通。

    5.3 使用C實現本地方法

    5.3.1生成頭文件

            使用javah命令生成JNI頭文件,這里需要注意是class路徑不是源代碼的路徑,并且要加上包名:

            這里生成了一個ophone_streaming_video_h264_H264decode.h,我們打開來看看:

    1. #include <jni.h>  
    2.   
    3. #ifndef _Included_ophone_streaming_video_h264_H264decode  
    4. #define _Included_ophone_streaming_video_h264_H264decode  
    5. #ifdef __cplusplus  
    6. extern "C" {  
    7. #endif  
    8.   
    9. JNIEXPORT jint JNICALL Java_ophone_streaming_video_h264_H264decode_DecodeOneFrame  
    10.   (JNIEnv *, jclass, jlong, jobject, jobject);  
    11.   
    12. JNIEXPORT jlong JNICALL Java_ophone_streaming_video_h264_H264decode_Initialize  
    13.   (JNIEnv *, jclass);  
    14.   
    15. JNIEXPORT void JNICALL Java_ophone_streaming_video_h264_H264decode_Destroy  
    16.   (JNIEnv *, jclass, jlong);  
    17.   
    18. #ifdef __cplusplus  
    19. }  
    20. #endif  
    21. #endif  

    5.3.2 實現本地方法

            之前已經生成了JNI頭文件,接下來只需要實現這個頭文件的幾個導出函數,這里以H264解碼器的實現為例:

    1. #include "ophone_streaming_video_h264_H264decode.h"  
    2. #include "H264Decode.h"  
    3.   
    4. JNIEXPORT jint JNICALL Java_ophone_streaming_video_h264_H264decode_DecodeOneFrame  
    5.   (JNIEnv * env, jclass obj, jlong decode, jobject pInBuffer, jobject pOutBuffer) {  
    6.   
    7.     H264Decode *pDecode = (H264Decode *)decode;  
    8.     unsigned char *In = NULL;unsigned char *Out = NULL;  
    9.     unsigned int InPosition = 0;unsigned int InRemaining = 0;unsigned int InSize = 0;  
    10.     unsigned int OutSize = 0;  
    11.     jint DecodeSize = -1;  
    12.   
    13.     jbyte *InJbyte = 0;  
    14.     jbyte *OutJbyte = 0;  
    15.   
    16.     jbyteArray InByteArrary = 0;  
    17.     jbyteArray OutByteArrary = 0;  
    18.   
    19.     //獲取Input/Out ByteBuffer相關屬性  
    20.     {  
    21.         //Input  
    22.         {  
    23.             jclass ByteBufferClass = env->GetObjectClass(pInBuffer);  
    24.             jmethodID PositionMethodId = env->GetMethodID(ByteBufferClass,"position","()I");  
    25.             jmethodID RemainingMethodId = env->GetMethodID(ByteBufferClass,"remaining","()I");  
    26.             jmethodID ArraryMethodId = env->GetMethodID(ByteBufferClass,"array","()[B");  
    27.               
    28.             InPosition = env->CallIntMethod(pInBuffer,PositionMethodId);  
    29.             InRemaining = env->CallIntMethod(pInBuffer,RemainingMethodId);  
    30.             InSize = InPosition + InRemaining;  
    31.               
    32.             InByteArrary = (jbyteArray)env->CallObjectMethod(pInBuffer,ArraryMethodId);  
    33.           
    34.             InJbyte = env->GetByteArrayElements(InByteArrary,0);  
    35.               
    36.             In = (unsigned char*)InJbyte + InPosition;  
    37.         }  
    38.   
    39.         //Output  
    40.         {  
    41.             jclass ByteBufferClass = env->GetObjectClass(pOutBuffer);  
    42.             jmethodID ArraryMethodId = env->GetMethodID(ByteBufferClass,"array","()[B");  
    43.             jmethodID ClearMethodId = env->GetMethodID(ByteBufferClass,"clear","()Ljava/nio/Buffer;");  
    44.               
    45.             //清理輸出緩存區  
    46.             env->CallObjectMethod(pOutBuffer,ClearMethodId);  
    47.   
    48.             OutByteArrary = (jbyteArray)env->CallObjectMethod(pOutBuffer,ArraryMethodId);  
    49.             OutJbyte = env->GetByteArrayElements(OutByteArrary,0);  
    50.   
    51.             Out = (unsigned char*)OutJbyte;  
    52.         }  
    53.     }  
    54.   
    55.     //解碼  
    56.     DecodeSize = pDecode->DecodeOneFrame(In,InRemaining,Out,OutSize);  
    57.   
    58.     //設置Input/Output ByteBuffer相關屬性  
    59.     {  
    60.         //Input  
    61.         {  
    62.             jclass ByteBufferClass = env->GetObjectClass(pInBuffer);  
    63.             jmethodID SetPositionMethodId = env->GetMethodID(ByteBufferClass,"position","(I)Ljava/nio/Buffer;");  
    64.               
    65.             //設置輸入緩沖區偏移  
    66.             env->CallObjectMethod(pInBuffer,SetPositionMethodId,InPosition + DecodeSize);  
    67.         }  
    68.   
    69.         //Output  
    70.         {  
    71.             jclass ByteBufferClass = env->GetObjectClass(pOutBuffer);  
    72.             jmethodID SetPositionMethodId = env->GetMethodID(ByteBufferClass,"position","(I)Ljava/nio/Buffer;");  
    73.   
    74.             //設置輸出緩沖區偏移  
    75.             env->CallObjectMethod(pOutBuffer,SetPositionMethodId,OutSize);  
    76.         }  
    77.     }  
    78.   
    79.     //清理  
    80.     env->ReleaseByteArrayElements(InByteArrary,InJbyte,0);  
    81.     env->ReleaseByteArrayElements(OutByteArrary,OutJbyte,0);  
    82.   
    83.     return DecodeSize;  
    84. }  
    85.   
    86. JNIEXPORT jlong JNICALL Java_ophone_streaming_video_h264_H264decode_Initialize  
    87.   (JNIEnv * env, jclass obj) {  
    88.   
    89.     H264Decode *pDecode = H264Decode::H264DecodeConstruct();  
    90.     return (jlong)pDecode;  
    91. }  
    92.   
    93. JNIEXPORT void JNICALL Java_ophone_streaming_video_h264_H264decode_Destroy  
    94.   (JNIEnv * env, jclass obj, jlong decode) {  
    95.   
    96.     H264Decode *pDecode = (H264Decode *)decode;  
    97.     if (pDecode)  
    98.     {  
    99.         delete pDecode;  
    100.         pDecode = NULL;  
    101.     }  
    102. }  

     5.3.3 編譯本地方法

            接下來,只需要把用C實現的本地方法編譯為動態鏈接庫,如果之前你用于移植的那個庫曾經移植到Symbian上過,那么編譯會相當簡單,因為NDK的編譯器和Symbian的編譯器一樣,都是采用GCC做交叉編譯器。

            首先,需要在$NDK"apps目錄下,創建一個項目目錄,這里創建了一個H264Decode目錄,在H264Decode目錄中,創建一個Android.mk文件:
     

    1. APP_PROJECT_PATH := $(call my-dir)  
    2. APP_MODULES      := H264Decode  


           接下來,需要在$NDK"source目錄下,創建源代碼目錄(這里的目錄名要和上面創建的項目目錄文件名相同),這里創建一個H264Decode目錄,然后把之前生成的JNI頭文件和你實現的本地方法相關頭文件和源代碼,都拷貝到   這個目錄下面。

              然后,我們編輯Android.mk文件:

    1. LOCAL_PATH := $(call my-dir)  
    2. include $(CLEAR_VARS)  
    3. LOCAL_MODULE    := H264Decode  
    4. 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  
    5. include $(BUILD_SHARED_LIBRARY)  


            關于Android.mk文件中,各個字段的解釋,可以參考$NDK"doc下的《OPHONE-MK.TXT》和《OVERVIEW.TXT》,里面有詳細的介紹。

            最后,我們啟動Cygwin,開始編譯:

           如果你看到了Install:**,這說明你的庫已經編譯好了。

           FAQ 2:
           如果編譯遇到下面錯誤,怎么辦?

    1. error: redefinition of typedef 'int8_t'  

       需要注釋掉你的代碼中“typedef signed char  int8_t;”,如果你的代碼之前是已經移植到了Mobile/Symbian上的話,很有可能遇到這個問題。
           

    5.4 編寫庫測試程序

        用Eclipse創建一個OPhone工程,在入口類中輸入如下代碼:

    1. /**    
    2.  * @author ophone 
    3.  * @email 3751624@qq.com 
    4.  */  
    5.   
    6. package ophone.streaming.video.h264;  
    7.   
    8. import java.io.File;  
    9. import java.io.FileInputStream;  
    10. import java.io.InputStream;  
    11. import java.nio.ByteBuffer;  
    12. import OPhone.app.Activity;  
    13. import OPhone.graphics.BitmapFactory;  
    14. import OPhone.os.Bundle;  
    15. import OPhone.os.Handler;  
    16. import OPhone.os.Message;  
    17. import OPhone.widget.ImageView;  
    18. import OPhone.widget.TextView;  
    19.   
    20. public class H264Example extends Activity {  
    21.       
    22.     private static final int VideoWidth = 352;  
    23.     private static final int VideoHeight = 288;  
    24.       
    25.     private ImageView ImageLayout = null;  
    26.     private TextView FPSLayout = null;  
    27.     private H264decode Decode = null;  
    28.     private Handler H = null;  
    29.     private byte[] Buffer = null;  
    30.       
    31.     private int DecodeCount = 0;  
    32.     private long StartTime = 0;  
    33.   
    34.     public void onCreate(Bundle savedInstanceState) {  
    35.           
    36.         super.onCreate(savedInstanceState);  
    37.         setContentView(R.layout.main);  
    38.           
    39.         ImageLayout = (ImageView) findViewById(R.id.ImageView);  
    40.         FPSLayout = (TextView) findViewById(R.id.TextView);  
    41.         Decode = new H264decode();  
    42.           
    43.         StartTime = System.currentTimeMillis();  
    44.           
    45.         new Thread(new Runnable(){  
    46.             public void run() {  
    47.                 StartDecode();  
    48.             }  
    49.         }).start();  
    50.           
    51.         H = new Handler(){  
    52.             public void handleMessage(Message msg) {  
    53.                 ImageLayout.invalidate();  
    54.                 ImageLayout.setImageBitmap(BitmapFactory.decodeByteArray(Buffer, 0, Buffer.length));  
    55.                   
    56.                 long Time = (System.currentTimeMillis()-StartTime)/1000;  
    57.                 if(Time > 0){  
    58.                     FPSLayout.setText("花費時間:" + Time + "秒  解碼幀數:" + DecodeCount + "  FPS:" + (DecodeCount/Time) );  
    59.                 }  
    60.             }  
    61.         };  
    62.     }  
    63.       
    64.     private void StartDecode(){  
    65.           
    66.         File h264file = new File("/tmp/Demo.264");  
    67.         InputStream h264stream = null;  
    68.         try {  
    69.               
    70.             h264stream = new FileInputStream(h264file);  
    71.             ByteBuffer pInBuffer = ByteBuffer.allocate(51200);//分配50k的緩存  
    72.             ByteBuffer pRGBBuffer = ByteBuffer.allocate(VideoWidth*VideoHeight*3);  
    73.               
    74.             while (h264stream.read(pInBuffer.array(), pInBuffer.position(), pInBuffer.remaining()) >= 0) {  
    75.                   
    76.                 pInBuffer.position(0);  
    77.                 do{  
    78.                     int DecodeLength = Decode.DecodeOneFrame(pInBuffer, pRGBBuffer);  
    79.                       
    80.                     //如果解碼成功,把解碼出來的圖片顯示出來  
    81.                     if(DecodeLength > 0 && pRGBBuffer.position() > 0){  
    82.                           
    83.                         //轉換RGB字節為BMP  
    84.                         BMPImage bmp = new BMPImage(pRGBBuffer.array(),VideoWidth,VideoHeight);  
    85.                         Buffer = bmp.getByte();  
    86.               
    87.                         H.sendMessage(H.obtainMessage());  
    88.   
    89.                         Thread.sleep(1);  
    90.                         DecodeCount ++;  
    91.                     }  
    92.                       
    93.                 }while(pInBuffer.remaining() > 10240);//確保緩存區里面的數據始終大于10k  
    94.                   
    95.                 //清理已解碼緩沖區  
    96.                 int Remaining = pInBuffer.remaining();  
    97.                 System.arraycopy(pInBuffer.array(), pInBuffer.position(), pInBuffer.array(), 0, Remaining);  
    98.                 pInBuffer.position(Remaining);  
    99.             }  
    100.               
    101.         } catch (Exception e1) {  
    102.             e1.printStackTrace();  
    103.         } finally {  
    104.             try{h264stream.close();} catch(Exception e){}  
    105.         }  
    106.           
    107.     }  
    108.   
    109.     protected void onDestroy() {  
    110.         super.onDestroy();  
    111.         Decode.Cleanup();  
    112.     }  
    113. }  

          BMPImage是一個工具類,主要用于把RGB序列,轉換為BMP圖象用于顯示:

    1. @author ophone  
    2.  * @email 3751624@qq.com  
    3. */  
    4.   
    5. package ophone.streaming.video.h264;  
    6.   
    7. import java.nio.ByteBuffer;  
    8.   
    9. public class BMPImage {  
    10.   
    11.     // --- 私有常量  
    12.     private final static int BITMAPFILEHEADER_SIZE = 14;  
    13.     private final static int BITMAPINFOHEADER_SIZE = 40;  
    14.   
    15.     // --- 位圖文件標頭  
    16.     private byte bfType[] = { 'B', 'M' };  
    17.     private int bfSize = 0;  
    18.     private int bfReserved1 = 0;  
    19.     private int bfReserved2 = 0;  
    20.     private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE;  
    21.   
    22.     // --- 位圖信息標頭  
    23.     private int biSize = BITMAPINFOHEADER_SIZE;  
    24.     private int biWidth = 176;  
    25.     private int biHeight = 144;  
    26.     private int biPlanes = 1;  
    27.     private int biBitCount = 24;  
    28.     private int biCompression = 0;  
    29.     private int biSizeImage = biWidth*biHeight*3;  
    30.     private int biXPelsPerMeter = 0x0;  
    31.     private int biYPelsPerMeter = 0x0;  
    32.     private int biClrUsed = 0;  
    33.     private int biClrImportant = 0;  
    34.       
    35.     ByteBuffer bmpBuffer = null;  
    36.       
    37.     public BMPImage(byte[] Data,int Width,int Height){  
    38.         biWidth = Width;  
    39.         biHeight = Height;  
    40.           
    41.         biSizeImage = biWidth*biHeight*3;  
    42.         bfSize = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE + biWidth*biHeight*3;  
    43.         bmpBuffer = ByteBuffer.allocate(BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE + biWidth*biHeight*3);  
    44.           
    45.         writeBitmapFileHeader();  
    46.         writeBitmapInfoHeader();  
    47.         bmpBuffer.put(Data);  
    48.     }  
    49.       
    50.     public byte[] getByte(){  
    51.         return bmpBuffer.array();  
    52.     }  
    53.       
    54.     private byte[] intToWord(int parValue) {  
    55.   
    56.         byte retValue[] = new byte[2];  
    57.   
    58.         retValue[0] = (byte) (parValue & 0x00FF);  
    59.         retValue[1] = (byte) ((parValue >> 8) & 0x00FF);  
    60.   
    61.         return (retValue);  
    62.     }  
    63.   
    64.     private byte[] intToDWord(int parValue) {  
    65.   
    66.         byte retValue[] = new byte[4];  
    67.   
    68.         retValue[0] = (byte) (parValue & 0x00FF);  
    69.         retValue[1] = (byte) ((parValue >> 8) & 0x000000FF);  
    70.         retValue[2] = (byte) ((parValue >> 16) & 0x000000FF);  
    71.         retValue[3] = (byte) ((parValue >> 24) & 0x000000FF);  
    72.   
    73.         return (retValue);  
    74.   
    75.     }  
    76.       
    77.     private void writeBitmapFileHeader () {  
    78.           
    79.         bmpBuffer.put(bfType);  
    80.         bmpBuffer.put(intToDWord (bfSize));  
    81.         bmpBuffer.put(intToWord (bfReserved1));  
    82.         bmpBuffer.put(intToWord (bfReserved2));  
    83.         bmpBuffer.put(intToDWord (bfOffBits));  
    84.           
    85.     }  
    86.       
    87.     private void writeBitmapInfoHeader () {  
    88.           
    89.         bmpBuffer.put(intToDWord (biSize));      
    90.         bmpBuffer.put(intToDWord (biWidth));      
    91.         bmpBuffer.put(intToDWord (biHeight));      
    92.         bmpBuffer.put(intToWord (biPlanes));      
    93.         bmpBuffer.put(intToWord (biBitCount));      
    94.         bmpBuffer.put(intToDWord (biCompression));      
    95.         bmpBuffer.put(intToDWord (biSizeImage));      
    96.         bmpBuffer.put(intToDWord (biXPelsPerMeter));      
    97.         bmpBuffer.put(intToDWord (biYPelsPerMeter));      
    98.         bmpBuffer.put(intToDWord (biClrUsed));      
    99.         bmpBuffer.put(intToDWord (biClrImportant));   
    100.           
    101.     }  
    102. }  


             測試程序完整工程在此暫不提供。

    5.5集成測試

            集成測試有兩點需要注意,在運行程序前,需要把動態庫復制到模擬器的/system/lib目錄下面,還需要把需要解碼的視頻傳到模擬器的/tmp目錄下。
           這里要明確的是,OPhone和Symbian的模擬器都做的太不人性化了,Symbian復制一個文件到模擬器中,要進一堆很深的目錄,OPhone的 更惱火,需要敲命令把文件傳遞到模擬器里,說實話,僅在這點上,Mobile的模擬器做的還是非常人性化的。
           命令:

    1. PATH=D:"OPhone"OPhone SDK"tools"  
    2. adb.exe remount  
    3. adb.exe push D:"Eclipse"workspace"H264Example"libs"armeabi"libH264Decode.so /system/lib  
    4. adb.exe push D:"Eclipse"workspace"H264Example"Demo.264 /tmp  
    5. pause  


          這里解釋一下abd push命令:
          adb push <本地文件路徑> <遠程文件路徑>    - 復制文件或者目錄到模擬器
          在Eclipse中,啟動庫測試程序,得到畫面如下:
     

            FAQ 3:

            模擬器黑屏怎么辦?
            這可能是由于模擬器啟動速度比較慢所引起的,所以需要多等一會。希望下個版本能夠改進。

    原文地址:http://www.ophonesdn.com/article/show/45;jsessionid=306BD3BE92F43DC693BEB09B0234B036





    Android開發完全講義(第2版)(本書版權已輸出到臺灣)

    http://product.dangdang.com/product.aspx?product_id=22741502



    Android高薪之路:Android程序員面試寶典 http://book.360buy.com/10970314.html


    新浪微博:http://t.sina.com.cn/androidguy   昵稱:李寧_Lining

    posted on 2009-09-20 17:54 銀河使者 閱讀(2640) 評論(1)  編輯  收藏 所屬分類: java 原創移動(mobile)

    評論

    # re: 流媒體程序開發之:H264解碼器移植到OPhone  回復  更多評論   

    不錯啊,頂一個
    2009-09-21 10:43 | xing
    主站蜘蛛池模板: 国产亚洲精品第一综合| 可以免费观看的国产视频| 国产亚洲精久久久久久无码AV| 中文字幕在线视频免费| 亚洲精品熟女国产| 日本视频免费在线| 野花香高清在线观看视频播放免费 | 久久久久亚洲AV成人无码网站| 午夜免费1000部| 日韩大片在线永久免费观看网站 | 猫咪免费观看人成网站在线| 一本岛高清v不卡免费一三区| 国内成人精品亚洲日本语音| 久久久久亚洲av无码专区蜜芽| 免费无码又爽又刺激毛片| 成人性生交大片免费看好| 亚洲日本VA午夜在线影院| 国产精品亚洲A∨天堂不卡| 天天摸天天碰成人免费视频| 国产成人免费ā片在线观看老同学 | 青青草国产免费久久久91| 精品国产免费一区二区三区香蕉| 亚洲综合色一区二区三区| 四虎成人免费网站在线| a级日本高清免费看| 亚洲av永久中文无码精品综合| 久久精品亚洲中文字幕无码网站| 九九精品成人免费国产片| 亚洲国产成人手机在线观看| 亚洲AV成人片色在线观看高潮| 免费人成在线观看视频播放| 国产成人精品免费午夜app| 91视频免费观看| 美女黄频视频大全免费的| 亚洲人成在久久综合网站| 亚洲精品蜜桃久久久久久| 免费乱码中文字幕网站| 免费高清小黄站在线观看| 国产四虎免费精品视频| 欧洲人成在线免费| 在线aⅴ亚洲中文字幕|