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

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

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

    淺陋見解,僅供參考。拋磚引玉,多加指教!
    石正
    一個計算機技術愛好者! 雖然我擁有了系統分析師的證書,但我仍然是一個計算機技術愛好者!
    posts - 119,comments - 73,trackbacks - 0
    摘要:本文詳細的解析了AVI文件的存儲結構,介紹了微軟提供的用來操作AVI文件的一組API使用方法,并通過例子代碼,演示了如何將一組靜態Bmp圖片合成一個avi視頻文件以及如何將一個avi視頻文件解析保存為一系列的bmp圖像文件。
     
    關鍵詞:avi文件 bmp圖像 vc
     
    AVI是音頻視頻交錯(Audio Video Interleaved)的英文縮寫,它是Microsoft公司開發的一種符合RIFF文件規范的數字音頻與視頻文件格式,原先用于Microsoft Video for Windows (簡稱VFW)環境,現在已被Windows 95/98、OS/2等多數操作系統直接支持。AVI格式允許視頻和音頻交錯在一起同步播放,支持256色和RLE壓縮,但AVI文件并未限定壓縮標準,因此,AVI文件格式只是作為控制界面上的標準,不具有兼容性,用不同壓縮算法生成的AVI文件,必須使用相應的解壓縮算法才能播放出來。常用的AVI播放驅動程序,主要是Microsoft Video for Windows或Windows 95/98中的Video 1,以及Intel公司的Indeo Video。
      在介紹AVI文件前,我們要先來看看RIFF文件結構。AVI文件采用的是RIFF文件結構方式,RIFF(Resource Interchange File Format,資源互換文件格式)是微軟公司定義的一種用于管理windows環境中多媒體數據的文件格式,波形音頻wave,MIDI和數字視頻AVI都采用這種格式存儲。構造RIFF文件的基本單元叫做數據塊(Chunk),每個數據塊包含3個部分,
    1 4字節的數據塊標記(或者叫做數據塊的ID)
    2 數據塊的大小
    3 數據
    整個RIFF文件可以看成一個數據塊,其數據塊ID為RIFF,稱為RIFF塊。一個RIFF文件中只允許存在一個RIFF塊。RIFF塊中包含一系列的子塊,其中有一種字塊的ID為“LIST”,稱為LIST,LIST塊中可以再包含一系列的子塊,但除了LIST塊外的其他所有的子塊都不能再包含子塊。
       RIFF和LIST塊分別比普通的數據塊多一個被稱為形式類型(Form Type)和列表類型(List Type)的數據域,其組成如下:
    1 4字節的數據塊標記(Chunk ID)
    2 數據塊的大小
    3 4字節的形式類型或者列表類型
    4 數據
     
    下面我們看看AVI文件的結構。AVI文件是目前使用的最復雜的RIFF文件,它能同時存儲同步表現的音頻視頻數據。AVI的RIFF塊的形式類型是AVI,它包含3個子塊,如下所述:
    1信息塊,一個ID為”hdrl”的LIST塊,定義AVI文件的數據格式。
    2數據塊,一個ID為 “movi”的LIST塊,包含AVI的音視頻序列數據
    3索引塊,ID為 “idxl”的子塊,定義 “movi”LIST塊的索引數據,是可選塊。
    AVI文件的結構如下圖所示,下面將具體介紹AVI文件的各子塊構造。
    1信息塊,信息塊包含兩個子塊,即一個ID為 avih 的子塊和一個ID 為 strl 的LIST塊。
     
    “avih”子塊的內容可由如下的結構定義:
     
    typedef struct
    {
     DWORD dwMicroSecPerFrame ; //顯示每楨所需的時間ns,定義avi的顯示速率
     DWORD dwMaxBytesPerSec; //      最大的數據傳輸率
     DWORD dwPaddingGranularity;    //記錄塊的長度需為此值的倍數,通常是2048
     DWORD dwFlages;                 //AVI文件的特殊屬性,如是否包含索引塊,音視頻數據是否交叉存儲
     DWORD dwTotalFrame;              //文件中的總楨數
     DWORD dwInitialFrames;           //說明在開始播放前需要多少楨
     DWORD dwStreams;                 //文件中包含的數據流種類
     DWORD dwSuggestedBufferSize; //建議使用的緩沖區的大小,
                                  //通常為存儲一楨圖像以及同步聲音所需要的數據之和
     DWORD dwWidth;                   //圖像寬
     DWORD dwHeight;                   //圖像高
     DWORD dwReserved[4];               //保留值
    }MainAVIHeader;
     
    “strl” LIST塊用于記錄AVI數據流,每一種數據流都在該LIST塊中占有3個子塊,他們的ID分別是”strh”,”strf”, “strd”;
    “strh”子塊由如下結構定義,
    typedef struct
    {
           FOURCC   fccType;      //4字節,表示數據流的種類 vids 表示視頻數據流
                                  //auds 音頻數據流
           FOURCC   fccHandler;//4字節 ,表示數據流解壓縮的驅動程序代號
           DWORD    dwFlags;      //數據流屬性
       WORD     wPriority;    //此數據流的播放優先級
           WORD     wLanguage;    //音頻的語言代號
           DWORD    dwInitalFrames;//說明在開始播放前需要多少楨
           DWORD    dwScale;       //數據量,視頻每楨的大小或者音頻的采樣大小
           DWORD    dwRate;        //dwScale /dwRate = 每秒的采樣數
           DWORD    dwStart;       //數據流開始播放的位置,以dwScale為單位
           DWORD    dwLength;      //數據流的數據量,以dwScale為單位
           DWORD    dwSuggestedBufferSize; //建議緩沖區的大小
           DWORD    dwQuality;      //解壓縮質量參數,值越大,質量越好
           DWORD    dwSampleSize;   //音頻的采樣大小
           RECT     rcFrame;        //視頻圖像所占的矩形
    }AVIStreamHeader;
    “strf”子塊緊跟在”strh”子塊之后,其結構視”strh”子塊的類型而定,如下所述;
    如果 strh子塊是視頻數據流,則 strf子塊的內容是一個與windows設備無關位圖的BIMAPINFO結構,如下
    typedef struct tagBITMAPINFO
    {
           BITMAPINFOHEADER     bmiHeader;
           RGBQUAD              bmiColors[1]; //顏色表
    }BITMAPINFO;
     
    typedef struct tagBITMAPINFOHEADER
    {
           DWORD biSize;
           LONG biWidth;
           LONG biHeight;
           WORD biPlanes;
           WORD biBitCount;
           DWORD biCompression;
           DWORD biSizeImage;
           LONG biXPelsPerMeter;
           LONG biYPelsPerMeter;
           DWORD biClrUsed;
           DWORD biClrImportant;
    }BITMAPINFOHEADER;
    如果 strh子塊是音頻數據流,則strf子塊的內容是一個WAVEFORMAT結構,如下
    typedef struct
    {
            WORD   wFormatTag; 
            WORD   nChannels;   //聲道數
            DWORD nSamplesPerSec; //采樣率
            DWORD nAvgBytesPerSec; //WAVE聲音中每秒的數據量
            WORD   nBlockAlign;     //數據塊的對齊標志
            WORD   biSize;          //此結構的大小
    }WAVEFORMAT
    “strd”子塊緊跟在strf子塊后,存儲供壓縮驅動程序使用的參數,不一定存在,也沒有固定的結構。
    “strl” LIST塊定義的AVI數據流依次將 “hdrl “ LIST 塊中的數據流頭結構與”movi” LIST塊中的數據聯系在一起,第一個數據流頭結構用于數據流0,第二個用于數據流1,依次類推。
    數據塊中存儲視頻和音頻數據流,數據可直接存于 “movi” LIST塊中。數據塊中音視頻數據按不同的字塊存放,其結構如下所述,
    音頻字塊
     “##wb”
     Wave 數據流
    視頻子塊中存儲DIB數據,又分為壓縮或者未壓縮DIB,
     “##db”
     RGB數據流
     
     “##dc”
     壓縮的圖像數據流
     
    看到了吧,avi文件的圖像數據可以是壓縮的,和非壓縮格式的。對于壓縮格式來說,也可采用不同的編碼,也許你曾經遇到有些avi沒法識別,就是因為編碼方式不一樣,如果沒有相應的解碼,你就沒法識別視頻數據。AVI的編碼方式有很多種,比較常見的有 mpeg2,mpeg4, divx等。
    索引塊,索引快包含數據塊在文件中的位置索引,能提高avi文件的讀寫速度,其中存放著一組AVIINDEXENTRY結構數據。如下,這個塊并不是必需的,也許不存在。
    typedef struct
     {
           DWORD ckid;          //記錄數據塊中子塊的標記
           DWORD dwFlags;        //表示chid所指子塊的屬性
           DWORD dwChunkOffset;   //子塊的相對位置
           DWORD dwChunkLength; //子塊長度
    };
    Ok,現在我相信你肯定會對AVI的文件結構已經很清楚了,在介紹完了AVI文件結構后,我們就來看看如何對avi文件進行讀寫了,為了對avi進行讀寫,微軟提供了一套API,總共50個函數,他們的用途主要有兩類,一個是avi文件的操作,一類是數據流streams的操作。
    1 打開和關閉文件
      AVIFileOpen ,AVIFileAddRef, AVIFileRelease
    2從文件中讀取文件信息
    通過AVIFileInfo可以獲取avi文件的一些信息,這個函數返回一個AVIFILEINFO結構
    通過AVIFileReadData可以用來獲取AVIFileInfo函數得不到的信息。這些信息也許不包含在文件的頭部,比如擁有file的公司和個人的名稱。
    3寫入文件信息
    可以通過AVIFileWriteData函數來寫入文件的一些額外信息。
    4打開和關閉一個流
      打開一個數據流就跟打開文件一樣,你可以通過 AVIFileGetStream函數來打開一個數據流,這個函數創建了一個流的接口,然后在該接口中保存了一個句柄。
     如果你想操作文件的某一個單獨的流,你可以采用AVIStreamOpenFromFile函數,這個函數綜合了AVIFileOpen和AVIFileGetStream函數。
     如果你想操作文件中的多個數據流,你就要首先AVIFileOpen,然后AVIFileGetStream。
     可以通過AVIStreamAddRef來增加stream接口的引用。
    通過AVIStreamRelease函數來關閉數據流。這個函數用來減少streams的引用計數,當計數減少為0時,刪除。
    5從流中讀取數據和信息
    AVIStreamInfo函數可以獲取數據的一些信息,該函數返回一個AVISTREAMINFO結構,該結構包含了數據的類型壓縮方法,建議的buffersize,回放的rate,以及一些description。
       如果數據流還有一些其它的額外的信息,你可以通過AVIStreamReadData函數來獲取。應用程序分配一個內存,傳遞給這個函數,然后這個函數會通過這個內存返回數據流的信息,額外的信息可能包括數據流的壓縮和解壓縮的方法,你可以通過AVIStreamDataSize宏來回去需要申請內存塊的大小。
       可以通過AVIStreamReadFormat函數獲取數據流的格式信息。這個函數通過指定的內存返回數據流的格式信息,比如對于視頻流,這個buffer包含了一個BIMAPINFO結構,對于音頻流,內存塊包含了WAVEFORMATEX或者PCMAVEFORMAT結構。你可以通過給AVIStreamReadFormat傳遞一個空buffer就可以獲取buffer的大小。也可以通過AVIStreamFormatSize宏。
      可以通過AVIStreamRead函數來返回多媒體的數據。這個函數將數據復制到應用程序提供的內存中,對于視頻流,這個函數返回圖像禎,對于音頻流,這個函數返回音頻的sample數據。可以通過給AVIStreamRead傳遞一個NULL的buffer來獲取需要的buffer的大小。也可以通過AVIStreamSampleSize宏來獲取buffer的大小。
      有些AVI數據流句柄可能需要在啟動數據流的前要做一下準備工作,此時,我們可以調用AVIStreamBeginStreaming函數來告知AVI數據流handle來申請分配它需要的一些資源。在完畢后,調用AVIStreamEndStreamming函數來釋放資源。
    6操作壓縮的視頻數據
      如果你要演示一禎或者幾禎壓縮視頻圖像時,你可以調用AVIStreamRead函數,將獲取的數據傳遞給DrawDib函數來顯示圖像。這些函數可以顯示壓縮和未壓縮的圖像。
     AVIFile也提供了一個函數AVIStreamGetFrameOpen,來獲取未壓縮的視頻禎,這個函數創建了內存來獲取未壓縮的數據。也可以通過AVIStreamGetFrame函數來解壓縮一個單獨的視頻禎。這個函數可以解壓縮某一禎圖像,然后將數據以一個BIMAPINFOHEADER結構返回。當你調用完AVIStreamGetFrame函數后,要調用AVIStreamGetFrameClose函數釋放上一個函數申請的資源。
    7根據已存在的數據流創建文件
       創建一個包含多個數據流的文件的方法就是整合多個數據流,將其寫入一個新文件。這些數據流可以是內存中的數據,也可以是存在于另一個文件中。
      我們可以用AVISave這個函數來build一個文件。這個函數可以創建一個文件,并且將指定的多個數據流按照指定的順序寫入文件,你也可以通過AVISaveV函數來創建一個新的文件,這個函數的功能和AVISave的功能一樣,主要區別是AVISaveV采用的數據流數組,而AVISave是單個的數據流,多次保存。
      我們可以調用AVISaveOptions函數來顯示一個對話框,可以讓用戶來選擇壓縮方式。
      我們可以在調用AVISave和AVISaveV函數時指定一個回調函數,用來顯示avi文件的生成進度,可以讓用戶隨時地取消生成avi文件。
      我們可以調用GetSaveFileNamePreview函數來顯示保存的對話框讓用戶選擇保存的文件名。
      通過AVIMakeFileFromStreams函數我們可以創建一個虛擬的文件句柄,其他的avi函數可以通過這個虛擬的文件句柄來操作文件中的數據流,操作完畢要記得調用AVIFileRelease釋放。
    8向文件寫入一個數據流
      我們可以通過AVIFileCreateStream函數來在一個新文件或者已經存在的文件中創建一個數據流。這個函數根據AVISTREAMINFO結構定義了新的數據流,并為新的數據流創建一個接口,返回接口的指針。
      在寫入新的數據前,一定要指定流的格式信息,通過AVIStreamSetFormat函數,當設置一個視頻流的時候,一定要使用BIMAPINFO結構來設置,音頻就用WAVEFORMAT。
      然后我們就可以通過AVIStreamWrite函數將我們的多媒體數據寫入數據流了。這個函數將應用程序提供的內存數據復制到指定的流。缺省的avi handler將數據寫入流的最后。
      如果你有其他額外的信息需要寫入流,你可以調用AVIFileWriteData或者AVIStreamWriteData,
     最后記得在完成數據寫入后,要調用AVIStreamRelease。
    9數據流中的禎的位置
     尋找起始禎:
      可以通過AVIStreamStart函數來獲取第一禎包含的sample number。也可以通過AVIStreamInfo函數來獲取這個信息,這個函數的AVISTREAMINFO結構中包含了dwStart,可以通過AVIStreamStartTime宏來獲取第一個sample。
      可以通過AVIStreamLength函數來獲取流的長度。這個函數返回流中的sample的數目。也可以通過AVIStreamInfo函數來獲取這些信息,可以通過AVIStreamLengthTime宏來獲取流的長度,毫秒。
      在視頻流中,一個sample對應著一禎圖像,所以,有時這些sample中沒有視頻數據,如果你調用AVIStreamRead函數來數據,可能返回NULL,也可以通過AVIStreamFindSample通過指定FIND_ANY標志來查找指定的sample。
    查找關鍵禎
    通過AVIStreamFindSample函數查找符合要尋找的sample,然后可以通過下面的宏判斷是否關鍵禎。
    在time和sample間互相切換
     AVIStreamSampleToTime這個函數可以將smaple轉換成毫秒。對于視頻,這個值代表的是這個禎開始播放的時間。
     
    在了解了上面的知識后,我們對avi的文件結構以及如何操作avi文件心里就明白了,下面我們可以開始我們的編程了。我們要做兩件事情,1如何將一組靜態的bmp位圖合成一個avi的視頻文件,2 如何將一個未壓縮的avi文件解析成一幅幅位圖。
     示例程序界面如下:
     
    下面的函數演示了如何將一個文件夾下面的所有bmp文件都保存為一個avi文件,函數的第一個參數是要生成的AVI的文件名,第二個參數是存放bmp文件的文件夾名,這個函數會枚舉該文件夾下的所有bmp文件,合成一個AVI文件。
     
    void Cbmp2aviDlg::AVItoBmp(CString strAVIFileName, CString   strBmpDir)
    {
           // TODO: 在此添加控件通知處理程序代碼
           AVIFileInit();
           PAVIFILE avi;
           int res=AVIFileOpen(&avi, strAVIFileName, OF_READ, NULL);
        int n = GetLastError();
           if (res!=AVIERR_OK)
           {
                  //an error occures
                  if (avi!=NULL)
                         AVIFileRelease(avi);
                  return ;
           }
               AVIFILEINFO avi_info;
           AVIFileInfo(avi, &avi_info, sizeof(AVIFILEINFO));
           PAVISTREAM pStream;
           res=AVIFileGetStream(avi, &pStream, streamtypeVIDEO /*video stream*/,
                  0 /*first stream*/);
           if (res!=AVIERR_OK)
           {
                  if (pStream!=NULL)
                         AVIStreamRelease(pStream);
                  AVIFileExit();
                  return ;
           }
     
           //do some task with the stream
           int iNumFrames;
           int iFirstFrame;
           iFirstFrame=AVIStreamStart(pStream);
           if (iFirstFrame==-1)
           {
                  //Error getteing the frame inside the stream
                  if (pStream!=NULL)
                         AVIStreamRelease(pStream);
                  AVIFileExit();
                  return ;
           }
           iNumFrames=AVIStreamLength(pStream);
           if (iNumFrames==-1)
           {
                  //Error getteing the number of frames inside the stream
                  if (pStream!=NULL)
                         AVIStreamRelease(pStream);
                  AVIFileExit();
                  return ;
           }
     
           //getting bitmap from frame
           BITMAPINFOHEADER bih;
           ZeroMemory(&bih, sizeof(BITMAPINFOHEADER));
     
           bih.biBitCount=24;    //24 bit per pixel
           bih.biClrImportant=0;
           bih.biClrUsed = 0;
           bih.biCompression = BI_RGB;
           bih.biPlanes = 1;
           bih.biSize = 40;
           bih.biXPelsPerMeter = 0;
           bih.biYPelsPerMeter = 0;
           //calculate total size of RGBQUAD scanlines (DWORD aligned)
           bih.biSizeImage = (((bih.biWidth * 3) + 3) & 0xFFFC) * bih.biHeight ;
     
           PGETFRAME pFrame;
           pFrame=AVIStreamGetFrameOpen(pStream, NULL );
     
           AVISTREAMINFO streaminfo;
           AVIStreamInfo(pStream,&streaminfo,sizeof(AVISTREAMINFO));
     
           //Get the first frame
           BITMAPINFOHEADER bih2;
           long lsize = sizeof(bih2);
           int index=0;
           for (int i=iFirstFrame; i<iNumFrames; i++)
           {
                  index= i-iFirstFrame;
                   BYTE* pDIB = (BYTE*) AVIStreamGetFrame(pFrame, index);                   //
            AVIStreamReadFormat(pStream,index,&bih2,&lsize);
                  BITMAPFILEHEADER stFileHdr;
     
     
                  BYTE* Bits=new BYTE[bih2.biSizeImage];
            AVIStreamRead(pStream,index,1,Bits,bih2.biSizeImage,NULL,NULL);
                  //RtlMoveMemory(Bits, pDIB + sizeof(BITMAPINFOHEADER), bih2.biSizeImage);
     
            bih2.biClrUsed =0;
                  stFileHdr.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
                  stFileHdr.bfSize=sizeof(BITMAPFILEHEADER);
                  stFileHdr.bfType=0x4d42;
     
                  CString FileName;
                  FileName.Format("Frame-%05d.bmp", index);
            CString strtemp = strBmpDir;
            strtemp += "\\";
            strtemp += FileName;
                  FILE* fp=_tfopen(strtemp ,_T("wb"));
                  fwrite(&stFileHdr,1,sizeof(BITMAPFILEHEADER),fp);
                  fwrite(&bih2,1,sizeof(BITMAPINFOHEADER),fp);
                  int ff = fwrite(Bits,1,bih2.biSizeImage,fp);
                  int e = GetLastError();
                  fclose(fp);
                  /////
                  delete Bits;
                  //CreateFromPackedDIBPointer(pDIB, index);
           }
     
           AVIStreamGetFrameClose(pFrame);
     
           //close the stream after finishing the task
           if (pStream!=NULL)
                  AVIStreamRelease(pStream);
     
           AVIFileExit();
    }
    下面的這個函數演示了如何將AVI文件中的每一楨圖像單獨取出來,保存為bmp文件。函數的頭一個參數是avi文件名,第二個參數是存放bmp文件的文件夾。
    //生成avi
    void Cbmp2aviDlg::BMPtoAVI(CString szAVIName, CString strBmpDir)
    {
           CFileFind finder;
           strBmpDir += _T("\\*.*");
           AVIFileInit();  
           AVISTREAMINFO strhdr;
           PAVIFILE pfile;
           PAVISTREAM ps;
           int nFrames =0;
           HRESULT hr;
     
           BOOL bFind = finder.FindFile(strBmpDir);
           while(bFind)
           {
                  bFind = finder.FindNextFile();
                  if(!finder.IsDots() && !finder.IsDirectory())
                  {
                         CString str = finder.GetFilePath();
                         FILE *fp = fopen(str,"rb");
                         BITMAPFILEHEADER bmpFileHdr;
                         BITMAPINFOHEADER bmpInfoHdr;
                         fseek( fp,0,SEEK_SET);
                         fread(&bmpFileHdr,sizeof(BITMAPFILEHEADER),1, fp);
                         fread(&bmpInfoHdr,sizeof(BITMAPINFOHEADER),1, fp);
     
                         BYTE *tmp_buf = NULL;
                         if(nFrames ==0 )
                         {
                               AVIFileOpen(&pfile,szAviName,OF_WRITE | OF_CREATE,NULL);
                                _fmemset(&strhdr, 0, sizeof(strhdr));
                                strhdr.fccType                = streamtypeVIDEO;// stream type
                                strhdr.fccHandler             = 0;
                                strhdr.dwScale                = 1;
                                strhdr.dwRate                 = 15;                 // 15 fps
                                strhdr.dwSuggestedBufferSize = bmpInfoHdr.biSizeImage ;
                                SetRect(&strhdr.rcFrame, 0, 0, bmpInfoHdr.biWidth, bmpInfoHdr.biHeight);
     
                                // And create the stream;
                                hr = AVIFileCreateStream(pfile,&ps,&strhdr);        
                   // hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));
                        }
                         tmp_buf = new BYTE[bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3];
                       fread(tmp_buf, 1, bmpInfoHdr.biWidth * bmpInfoHdr.biHeight * 3, fp);
                         hr = AVIStreamSetFormat(ps,nFrames,&bmpInfoHdr,sizeof(bmpInfoHdr));
                         hr = AVIStreamWrite(ps,       // stream pointer
                                  nFrames ,                          // time of this frame
                                       1,                         // number to write
                                       (LPBYTE) tmp_buf,
                                       bmpInfoHdr.biSizeImage , // size of this frame
                                       AVIIF_KEYFRAME,                    // flags....
                                       NULL,
                                       NULL);
     
                         nFrames ++;
                         fclose(fp);
     
                  }
           }
     
           AVIStreamClose(ps);
     
          if(pfile != NULL)
          AVIFileRelease(pfile);
     
            AVIFileExit();
    }
     
    結束語:
    以上代碼在 vc 6.0 和windows xp平臺調試通過。這兩個函數你可以直接在你的程序中使用,更詳細的代碼可以參見隨著本文附上的示例源碼。這里我要指出的是,這個AVI文件和bmp互相轉換過程中,avi中的視頻數據都是存放的是沒有壓縮的數據,如果你要分解AVI文件是經過壓縮編碼,比如,DVSD,MPEG4編碼,首先你要采用相應的解碼器對視頻數據解碼,然后將解碼過的數據保存為bmp文件。好了,關于avi文件的介紹就到這里結束了
    posted on 2007-10-24 10:22 石正 閱讀(695) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    淺陋見解,僅供參考。拋磚引玉,多加指教!
    主站蜘蛛池模板: 成人免费毛片观看| 国产偷国产偷亚洲清高动态图 | 亚洲免费一级视频| 亚洲日韩看片无码电影| 亚洲麻豆精品国偷自产在线91| 区三区激情福利综合中文字幕在线一区亚洲视频1 | 免费国产污网站在线观看| 亚洲国产高清美女在线观看 | 亚洲综合久久一本伊伊区| 亚洲精品在线免费观看视频 | 在线人成免费视频69国产| 精品亚洲456在线播放| AV在线亚洲男人的天堂| 18禁网站免费无遮挡无码中文| 亚洲美女自拍视频| 免费在线观看亚洲| 2022免费国产精品福利在线 | 亚洲成A人片777777| 日韩a在线观看免费观看| 国产精品观看在线亚洲人成网| 久久亚洲精品视频| 麻豆国产入口在线观看免费| 免费一区二区无码东京热| 老子影院午夜伦不卡亚洲| 亚洲日韩国产精品无码av| 在线A亚洲老鸭窝天堂| 日本免费一区二区三区最新vr| 99视频在线免费| 亚洲一区二区三区在线观看蜜桃| 亚洲欧洲免费无码| 国产AV无码专区亚洲AV琪琪| 亚洲第一页在线观看| 日韩免费毛片视频| 91热久久免费精品99| 亚洲综合无码一区二区痴汉 | 久久亚洲私人国产精品| 久久精品国产精品亚洲艾草网美妙 | 亚洲人成人77777在线播放| 国产成人亚洲综合无码精品| 亚洲Av无码乱码在线观看性色 | 免费看又黄又无码的网站|