亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

零基礎讀懂視頻播放器控制原理——ffplay播放器源代碼分析(二)

2019-11-09 15:02:54
字體:
來源:轉載
供稿:網友

三、先拋五個問題

本文還是以問題拋問題的思路,以逐步對每個問題進行原理性分析,加深對音視頻解碼和播放的認識。以下這些問題也是每一個播放器所需要面對的基礎問題和原理: 

1.我們在觀看電影時發現,電影可以更換不同字幕,甚至不同音頻,比如中英文字幕和配音,最后在同一個畫面中進行顯示,視頻關于畫面、字幕和聲音是如何組合的?

其實每一個視頻文件,讀取出來之后發現,都會被區分不同的流。為了讓大家有更具體的理解,以FFMpeg中的代碼為例,AVMediaType定義了具體的流類型:

enum AVMediaType {    AVMEDIA_TYPE_VIDEO,  //視頻流    AVMEDIA_TYPE_AUDIO, //音頻流    AVMEDIA_TYPE_SUBTITLE, //字幕流};

利用av_read_frame讀取出音視頻幀之后,隨后就利用avcodec_decode_video2對視頻捷星解碼,或者調用avcodec_decode_audio4對音頻進行解碼,得到可以供渲染和顯示的音視頻原始數據。

圖像和字幕都將會以Surface或者texture的形式,就像Android中的SurfaceFlinger,將畫面不同模塊的顯示進行組合,生成一幅新的圖像,顯示在視頻畫面中。

 

2.既然視頻有幀率的概念,音頻有采樣率的概念,是否直接利用幀率就可以控制音視頻的同步了呢?

每一個視頻幀和音頻幀在時域上都對應于一個時間點,按道理來說只要控制每一個音視頻幀的播放時間,就可以實現同步。

但實際上,對每一幀顯示的時間上的精確控制是很難的,更何況音頻和視頻的解碼所需時間不同,極容易引起音視頻在時間上的不同步。

所以,播放器具體是如何做音視頻同步的呢?

 

3.視頻的音頻流、視頻流和字幕流,他們在時間上是連續的還是離散的?不同流的幀數相同嗎?

由于計算機只能數字模擬離散的世界,所以在時間上肯定是離散的。那既然是離散的,他們的幀數是否相同呢?

視頻可以理解為諸多音頻幀、視頻幀和字幕幀在時間上的序列,他們在時間上的時長,跟視頻總時長是相同的,但是由于每個幀解碼時間不同,必然會導致他們在每幀的時間間隔不相同。

音頻原始數據本身就是采樣數據,所以是有固定時鐘周期。但是視頻假如想跟音頻進行同步的話,可能會出現跳幀的情況,每個視頻幀播放時間差,都會起伏不定,不是恒定周期。

所以結論是,三者在視頻總時長上播放的幀數肯定是不一樣的。

 

4.視頻播放就是一系列的連續幀不停渲染。對視頻的控制操作包括:暫停和播放、快進和后退。那有沒有想過,每次快進/后退的幅度,以時間為量度好,還是以每次跳躍的幀數,就是每次快進是前進多長時間,還是前進多少幀。 時間 VS 幀數?

由上面問題分析,我們知道,視頻是以音頻流、視頻流和字幕流進行分流的,假如以幀數為基礎,由于不同流的幀數量不一定相同,以幀數為單位,很容易導致三個流播放的不一致。

因此以時間為量度,相對更好,直接搜尋mp4文件流,當前播放時間的前進或后退時長的seek時間點,隨后重新對文件流進行分流解析,就可以達到快進和后退之后的音視頻同步效果。

我們可以看到絕大部分播放器,快進/倒退都是以時長為步進的,我們可以看看ffplay是怎么樣的,以及是如何實現的。

 

5.上一節中,實現的簡單播放器,解碼和播放都是在同一個線程中,解碼速度直接影響播放速度,從而將直接造成播放不流暢的問題。那如何在解碼可能出現速度不均勻的情況下,進行流暢的視頻播放呢?

 

很容易想到,引入緩沖隊列,將視頻圖像渲染顯示和視頻解碼作為兩個線程,視頻解碼線程往隊列中寫數據,視頻渲染線程從隊列中讀取數據進行顯示,這樣就可以保證視頻是可以流程播放的。

因此需要采用音頻幀、視頻幀和字幕幀的三個緩沖隊列,那如何保證音視頻播放的同步呢?

PTS是視頻幀或者音頻幀的顯示時間戳,究竟是如何利用起來的,從而控制視頻幀、音頻幀以及字幕幀的顯示時刻呢?

那我們就可以探尋ffplay,究竟是如何去做緩沖隊列控制的。

所有以上五個問題,我們都將在對ffplay源代碼的探尋中,逐步找到更具體的解答。

四、ffplay代碼總體結構

圖6 ffplay代碼總體流程(參見原圖)

網上有人做了ffplay的總體流程圖,如圖6。有了這幅圖,代碼看起來,就會輕松了很多。流程中具體包含的細節如下:

1.啟動定時器Timer,計時器40ms刷新一次,利用SDL事件機制,觸發從圖像幀隊列中讀取數據,進行渲染顯示;

2.stream_componet_open函數中,av_read_frame()讀取到AVPacket,隨后放入到音頻、視頻或字幕Packet隊列中;

3.video_thread,從視頻packet隊列中獲取AVPacket并進行解碼,得到AVFrame圖像幀,放到VideoPicture隊列中。

4..audio_thread線程,同video_thread,對音頻Packet進行解碼;

5.subtitle_thread線程,同video_thread,對字幕Packet進行解碼。

 

五、視頻播放器的操作控制

視頻播放器的操作包括播放/暫停、快進/倒退、逐幀播放等,這些操作的實現原理是什么呢,下面對其從代碼層面逐個進行分析。

5.1 ffplay所定義的關鍵結構體VideoState

與FFmpeg解碼類似,定義了一個AVFormatContext結構體,用于存儲文件名、音視頻流、解碼器等字段,供全局進行訪問。

ffplay也定義了一個結構體VideoState,通過對VideoState的分析,就可以大體知道播放器基本實現原理。

typedef struct VideoState {       // Demux解復用線程,讀視頻文件stream線程,得到AVPacket,并對packet入棧       SDL_Thread *read_tid;         //視頻解碼線程,讀取AVPacket,decode 爬出可以成AVFrame并入隊       SDL_Thread *video_tid;       //視頻播放刷新線程,定時播放下一幀       SDL_Thread *refresh_tid;       int paused;  //控制視頻暫?;虿シ艠酥疚?nbsp;      int seek_req;  //進度控制標志       int seek_flags;       AVStream *audio_st;   //音頻流       PacketQueue audioq;  //音頻packet隊列       double audio_current_pts;  //當前音頻幀顯示時間       AVStream *subtitle_st; //字幕流       PacketQueue subtitleq;//字幕packet隊列        AVStream *video_st; //視頻流       PacketQueue videoq;//視頻packet隊列       double video_current_pts; ///當前視頻幀pts       double video_current_pts_drift;         VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE];  //解碼后的圖像幀隊列}

從VideoState結構體中可以看出:

1.解復用、視頻解碼和視頻刷新播放,分屬三個線程中,并行控制;

2.音頻流、視頻流、字幕流,都有自己的緩沖隊列,供不同線程讀寫,并且有自己的當前幀的PTS;

3.解碼后的圖像幀單獨放在pictq隊列當中,SDL利用其進行顯示。

其中PTS是什么呢,這在音視頻中是一個很重要的概念,直接決定視頻幀或音頻幀的顯示時間,下面具體介紹一下。

5.2 補充基礎知識——PTS和DTS

圖7 音視頻解碼分析 

圖7為輸出的音頻幀和視頻幀序列,每一幀都有PTS和DTS標簽,這兩個標簽究竟是什么意思呢?

DTS(Decode Time Stamp)和PTS(PResentation Time Stamp)都是時間戳,前者是解碼時間,后者是顯示時間,都是為視頻幀、音頻幀打上的時間標簽,以更有效地支持上層應用的同步機制。

也就是說,視頻幀或者音頻在解碼時,會記錄其解碼時間,視頻幀的播放時間依賴于PTS。

對于聲音來說 ,這兩個時間標簽是相同的;但對于某些視頻編碼格式,由于采用了雙向預測技術,DTS會設置一定的超時或延時,保證音視頻的同步,會造成DTS和PTS的不一致。

5.3 如何控制音視頻同步 

我們已經知道,視頻幀的播放時間其實依賴pts字段的,音頻和視頻都有自己單獨的pts。但pts究竟是如何生成的呢,假如音視頻不同步時,pts是否需要動態調整,以保證音視頻的同步?

下面先來分析,如何控制視頻幀的顯示時間的:

static void video_refresh(void *opaque){   //根據索引獲取當前需要顯示的VideoPicture  VideoPicture *vp = &is->pictq[is->pictq_rindex];  if (is->paused)      goto display; //只有在paused的情況下,才播放圖像  // 將當前幀的pts減去上一幀的pts,得到中間時間差  last_duration = vp->pts - is->frame_last_pts;  //檢查差值是否在合理范圍內,因為兩個連續幀pts的時間差,不應該太大或太小  if (last_duration > 0 && last_duration < 10.0) {    /* if duration of the last frame was sane, update last_duration in video state */    is->frame_last_duration = last_duration;  }  //既然要音視頻同步,肯定要以視頻或音頻為參考標準,然后控制延時來保證音視頻的同步,  //這個函數就做這個事情了,下面會有分析,具體是如何做到的。  delay = compute_target_delay(is->frame_last_duration, is);  //獲取當前時間  time= av_gettime()/1000000.0;   //假如當前時間小于frame_timer + delay,也就是這幀改顯示的時間超前,還沒到,就直接返回  if (time < is->frame_timer + delay)       return;  //根據音頻時鐘,只要需要延時,即delay大于0,就需要更新累加到frame_timer當中。  if (delay > 0)       /更新frame_timer,frame_time是delay的累加值       is->frame_timer += delay * FFMAX(1, floor((time-is->frame_timer) / delay));  SDL_LockMutex(is->pictq_mutex);  //更新is當中當前幀的pts,比如video_current_pts、video_current_pos 等變量  update_video_pts(is, vp->pts, vp->pos);  SDL_UnlockMutex(is->pictq_mutex);display:  /* display picture */  if (!display_disable)    video_display(is);}

 

函數compute_target_delay根據音頻的時鐘信號,重新計算了延時,從而達到了根據音頻來調整視頻的顯示時間,從而實現音視頻同步的效果。

static double compute_target_delay(double delay, VideoState *is){    double sync_threshold, diff;   //因為音頻是采樣數據,有固定的采用周期并且依賴于主系統時鐘,要調整音頻的延時播放較難控制。所以實際場合中視頻同步音頻相比音頻同步視頻實現起來更容易。   if (((is->av_sync_type == AV_SYNC_AUDIO_MASTER && is->audio_st) ||     is->av_sync_type == AV_SYNC_EXTERNAL_CLOCK)) {       //獲取當前視頻幀播放的時間,與系統主時鐘時間相減得到差值       diff = get_video_clock(is) - get_master_clock(is);       sync_threshold = FFMAX(AV_SYNC_THRESHOLD, delay);      //假如當前幀的播放時間,也就是pts,滯后于主時鐘      if (fabs(diff) < AV_NOSYNC_THRESHOLD) {         if (diff <= -sync_threshold)             delay = 0;      //假如當前幀的播放時間,也就是pts,超前于主時鐘,那就需要加大延時      else if (diff >= sync_threshold)        delay = 2 * delay;      }   }   return delay;}

 圖8 音視頻幀顯示序列

所以這里的流程就很簡單了,圖8簡單畫了一個音視頻幀序列,想表達的意思是,音頻幀數量和視頻幀數量不一定對等,另外每個音頻幀的顯示時間在時間上幾乎對等,每個視頻幀的顯示時間,會根據具體情況有延時顯示,這個延時就是有上面的compute_target_delay函數計算出來的。

計算延遲后,更新pts的代碼如下:

static void update_video_pts(VideoState *is, double pts, int64_t pos) {    double time = av_gettime() / 1000000.0;    /* update current video pts */    is->video_current_pts = pts;    is->video_current_pts_drift = is->video_current_pts - time;    is->video_current_pos = pos;    is->frame_last_pts = pts;}

 

整個流程可以概括為:

顯示第一幀視頻圖像;根據音頻信號,計算出第二幀的delay時間,更新該幀的pts。當pts到達后,顯示第二幀視頻圖像。重復以上步驟,到最后一幀

也許在這里仍然會讓人很困惑,為什么單單根據主時鐘,就可以播放下一幀所需要的延時呢?

其實視頻是具備一定長度的播放流,具體可以分為音頻流、視頻流和字幕流,三者同時在一起播放形成了視頻,當然他們總的播放時間是跟視頻文件的播放時長是一樣的。

由于音頻流本身是pwm采樣數據,以固定的頻率播放,這個頻率是跟主時鐘相同或是它的分頻,從時間的角度來看,每個音頻幀是自然均勻流逝。

所以音頻的話,直接按照主時鐘或其分頻走就可以了。

視頻,要根據自己的顯示時間即pts,跟主時鐘當前的時間進行對比,確定是超前還是滯后于系統時鐘,從而確定延時,隨后進行準確的播放,這樣就可以保證音視頻的同步了。

 那接下來,還有一個問題,計算出延時之后,難道需要sleep一下做延遲顯示嗎?

其實并不是如此,上面分析我們知道delay會更新到當前需要更新視頻幀的pts (video_current_pts),對當前AVFrame進行顯示前,先檢測其pts時間,假如還沒到,就不進行顯示了,直接return。直到下一次刷新,重新進行檢測(ffplay采用的40ms定時刷新)。

代碼如下,未到更新后的pts時間( is->frame_timer + dela),直接return:

if (av_gettime()/1000000.0 < is->frame_timer + delay)      return;

 

  那接下來就是分析如何播放視頻幀,就很簡單了,只是這里多加了一個字幕流的處理:

static void video_image_display(VideoState *is){    VideoPicture *vp;   SubPicture *sp;   AVPicture pict;   SDL_Rect rect;   int i;   vp = &is->pictq[is->pictq_rindex];   if (vp->bmp) {       //字幕處理       if (is->subtitle_st) {}                     }   //計算圖像的顯示區域   calculate_display_rect(&rect, is->xleft, is->ytop, is->width, is->height, vp);   //顯示圖像   SDL_DisplayYUVOverlay(vp->bmp, &rect);   //將pic隊列的指針向前移動一個位置   pictq_next_picture(is);   }

   

VIDEO_PICTURE_QUEUE_SIZE  只設置為4,很快就會用完了。數據滿了如何重新更新呢

一旦檢測到超出隊列大小限制,就處于等待狀態,直到pictq被取出消費,從而避免開啟播放器,就把整個文件全部解碼完,這樣會代碼會很吃內存。

static int queue_picture(VideoState *is, AVFrame *src_frame, double pts1, int64_t pos){/* keep the last already displayed picture in the queue */while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE - 2 &&      !is->videoq.abort_request) {    SDL_CondWait(is->pictq_cond, is->pictq_mutex);   }   SDL_UnlockMutex(is->pictq_mutex);}

 

5.4 如何控制視頻的播放和暫停?

static void stream_toggle_pause(VideoState *is){    if (is->paused) {       //由于frame_timer記下來視頻從開始播放到當前幀播放的時間,所以暫停后,必須要將暫停的時間( is->video_current_pts_drift - is->video_current_pts)一起累加起來,并加上drift時間。     is->frame_timer += av_gettime() / 1000000.0 + is->video_current_pts_drift - is->video_current_pts;     if (is->read_pause_return != AVERROR(ENOSYS)) {     //并更新video_current_pts        is->video_current_pts = is->video_current_pts_drift + av_gettime() / 1000000.0;       }    //drift其實就是當前幀的pts和當前時間的時間差    is->video_current_pts_drift = is->video_current_pts - av_gettime() / 1000000.0;    }    //paused取反,paused標志位也會控制到圖像幀的展示,按一次空格鍵實現暫停,再按一次就實現播放了。    is->paused = !is->paused;}

 特別說明:paused標志位控制著視頻是否播放,當需要繼續播放的時候,一定要重新更新當前所需要播放幀的pts時間,因為這里面要加上已經暫停的時間。

5.5 逐幀播放是如何做的?

在視頻解碼線程中,不斷通過stream_toggle_paused,控制對視頻的暫停和顯示,從而實現逐幀播放:

static void step_to_next_frame(VideoState *is){   //逐幀播放時,一定要先繼續播放,然后再設置step變量,控制逐幀播放   if (is->paused)      stream_toggle_pause(is);//會不斷將paused進行取反   is->step = 1;}

其原理就是不斷的播放,然后暫停,從而實現逐幀播放:

static int video_thread(void *arg){  if (is->step)    stream_toggle_pause(is);      ……………………  if (is->paused)    goto display;//顯示視頻  }}

5.6 快進和后退

關于快進/后退,首先拋出兩個問題:

1. 快進以時間為維度還是以幀數為維度來對播放進度進行控制呢?

2.一旦進度發生了變化,那么當前幀,以及AVFrame隊列是否需要清零,整個對stream的流是否需要重新來進行控制呢?

ffplay中采用以時間為維度的控制方法。對于快進和后退的控制,都是通過設置VideoState的seek_req、seek_pos等變量進行控制

 do_seek:   //實際上是計算is->audio_current_pts_drift + av_gettime() / 1000000.0,確定當前需要播放幀的時間值   pos = get_master_clock(cur_stream);   pos += incr;  //incr為每次快進的步進值,相加即可得到快進后的時間點   stream_seek(cur_stream, (int64_t)(pos * AV_TIME_BASE), (int64_t)(incr * AV_TIME_BASE), 0);

關于stream_seek的代碼如下,其實就是設置VideoState的相關變量,以控制read_tread中的快進或后退的流程:

/* seek in the stream */static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_bytes){  if (!is->seek_req) {  is->seek_pos = pos;  is->seek_rel = rel;  is->seek_flags &= ~AVSEEK_FLAG_BYTE;  if (seek_by_bytes)    is->seek_flags |= AVSEEK_FLAG_BYTE;  is->seek_req = 1;}}

stream_seek中設置了seek_req標志,就直接進入前進/后退控制流程了,其原理是調用avformat_seek_file函數,根據時間戳控制索引點,從而控制需要顯示的下一幀:

static int read_thread(void *arg){//當調整播放進度以后if (is->seek_req) {   int64_t seek_target = is->seek_pos;   int64_t seek_min    = is->seek_rel > 0 ? seek_target - is->seek_rel + 2: INT64_MIN;   int64_t seek_max    = is->seek_rel < 0 ? seek_target - is->seek_rel - 2: INT64_MAX;  //根據時間抽查找索引點位置,定位到索引點之后,下一幀的讀取直接從這里開始,就實現了快進/后退操作  ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);  if (ret < 0) {     fprintf(stderr, "%s: error while seeking/n", is->ic->filename);  } else {  //查找成功之后,就需要清空當前的PAcket隊列,包括音頻、視頻和字幕     if (is->audio_stream >= 0) {        packet_queue_flush(&is->audioq);        packet_queue_put(&is->audioq, &flush_pkt);     }     if (is->subtitle_stream >= 0) {//處理字幕stream        packet_queue_flush(&is->subtitleq);        packet_queue_put(&is->subtitleq, &flush_pkt);    }    if (is->video_stream >= 0) {       packet_queue_flush(&is->videoq);       packet_queue_put(&is->videoq, &flush_pkt);    }  }  is->seek_req = 0;  eof = 0;  }}

另外從上面代碼中發現,每次快進后退之后都會對audioq、videoq和subtitleq進行flush清零,也是相當于重新開始,保證緩沖隊列中的數據的正確性。 

對于音頻,開始仍然有些困惑,因為在暫停的時候,沒有看到對音頻的控制,是如何控制的呢?

后來發現,其實暫停的時候設置了is->paused變量,解復用和音頻解碼和播放都依賴于is->paused變量,所以音頻和視頻播放都隨之停止了。

 

 

六、 這次分析ffplay代碼的反省總結:

1.基礎概念和原理積累,最開始接觸FFmpeg,因為其涉及的概念很多,看起來有種無從下手的感覺。這時候必須從基本模塊入手,逐步理解更多,一定的量積累,就會產生一些質變,更好的理解視頻編解碼機制;

2.一定要首先看懂代碼總體架構和流程,隨后針對每個細節點進行深入分析,會極大提高看代碼效率。會畫一些框圖是非常重要的,比如下面這張,所以簡要的流程圖要比注重細節的uml圖要方便得多;

3.看FFmpeg代碼,在PC端上調試,會快捷很多。假如要在Android上,調用jni來看代碼,效率就會很低。

 

參考文章:

 雷神的文章(多媒體入門開發必看):

http://blog.csdn.net/leixiaohua1020/article/details/15811977


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
一个人www欧美| 亚洲午夜精品视频| 国产最新精品视频| 欧美激情va永久在线播放| 91黑丝高跟在线| 黑人狂躁日本妞一区二区三区| 最近2019中文字幕在线高清| 自拍偷拍亚洲区| 91亚洲人电影| 久久久久久久影院| 国产精品香蕉av| 欧美午夜美女看片| xxx成人少妇69| 精品人伦一区二区三区蜜桃免费| 亚洲性生活视频在线观看| 久久久久久网址| 精品毛片网大全| 性夜试看影院91社区| 中文字幕成人精品久久不卡| 日韩少妇与小伙激情| 久热精品视频在线免费观看| 免费av在线一区| 色婷婷av一区二区三区在线观看| 亚洲欧美一区二区三区情侣bbw| 亚洲黄色av女优在线观看| 777精品视频| 日韩欧美aⅴ综合网站发布| 欧美影院成年免费版| 中文字幕久久精品| 国产亚洲欧美一区| 热99精品里视频精品| 97视频在线观看网址| 欧美一级黑人aaaaaaa做受| 国产精品久久久久久av下载红粉| 久久天天躁狠狠躁夜夜躁| 精品无人区太爽高潮在线播放| 国产999精品视频| 亚洲精品国产综合久久| 狠狠躁夜夜躁久久躁别揉| 亚洲色图激情小说| 久久久久成人网| 国产精品极品尤物在线观看| 国产a∨精品一区二区三区不卡| 欧美视频在线观看免费| 国产成+人+综合+亚洲欧美丁香花| 国产国产精品人在线视| 色综合久久88色综合天天看泰| 日韩大陆欧美高清视频区| 日韩欧美精品网址| 国内免费久久久久久久久久久| 久热精品视频在线观看一区| 日产精品久久久一区二区福利| 精品国产91久久久久久老师| 国产精品麻豆va在线播放| 亚洲欧美日韩成人| 日韩在线观看网站| 日韩中文字幕精品视频| 国产美女久久精品| 亚洲成人av中文字幕| 精品亚洲一区二区三区四区五区| 亚洲精品一区在线观看香蕉| 国产热re99久久6国产精品| 欧美日韩一区二区精品| 日韩精品免费观看| 欧美另类极品videosbest最新版本| 亚洲视频在线观看免费| 亚洲自拍高清视频网站| 中文亚洲视频在线| 国产精品 欧美在线| 久久久久女教师免费一区| 超碰精品一区二区三区乱码| 国产精品丝袜一区二区三区| 5252色成人免费视频| 国内精品久久久久影院 日本资源| 国产精品丝袜视频| 亚洲性xxxx| 日韩中文字幕第一页| 黑人巨大精品欧美一区免费视频| 欧美国产日韩一区二区在线观看| 久久偷看各类女兵18女厕嘘嘘| 中文字幕在线精品| 高清欧美性猛交xxxx黑人猛交| 日本免费久久高清视频| 97色在线视频| 亚洲欧美另类在线观看| 高潮白浆女日韩av免费看| 欧美孕妇孕交黑巨大网站| 韩剧1988在线观看免费完整版| 久久99久国产精品黄毛片入口| 亚洲石原莉奈一区二区在线观看| 欧美国产乱视频| 国外视频精品毛片| 欧美精品www在线观看| 日日噜噜噜夜夜爽亚洲精品| 日韩欧美亚洲一二三区| 欧美黄色小视频| 欧美剧在线观看| 国产亚洲精品综合一区91| 亚洲91av视频| 国产成人精品久久亚洲高清不卡| 欧美风情在线观看| 国产不卡av在线免费观看| 亚洲人成毛片在线播放| 欧美国产在线电影| 欧美在线观看视频| 国产一区av在线| 91欧美精品成人综合在线观看| 精品久久久久久久久国产字幕| 欧美精品生活片| 亚洲欧美国内爽妇网| 日韩免费在线免费观看| 日韩电影第一页| 亚洲国产精品小视频| 一区二区三区 在线观看视| 欧美国产精品人人做人人爱| 日本中文字幕久久看| 国产精品视频精品视频| 精品亚洲男同gayvideo网站| 国产精品69久久| 久久男人的天堂| 亚洲国产女人aaa毛片在线| 成人亚洲综合色就1024| 亚洲第一二三四五区| 欧美孕妇性xx| 久久精品国产v日韩v亚洲| 日韩电影在线观看永久视频免费网站| 69久久夜色精品国产69| 国产精品欧美激情在线播放| 国产中文字幕日韩| 国产精品自产拍在线观看| 最近2019年手机中文字幕| 亚洲一区二区三区四区视频| 亚洲国产精品小视频| 国产精品色视频| 亚洲国产精品久久久久秋霞不卡| 国产精品美女久久久免费| 久久99精品国产99久久6尤物| 欧美成人午夜激情在线| 欧美中文在线免费| xvideos成人免费中文版| 精品久久久久久久久中文字幕| 国产精品亚洲综合天堂夜夜| 日韩在线观看av| 国产精品h片在线播放| 亚洲欧美制服丝袜| 亚洲免费影视第一页| 日日骚av一区| 国产精品第一区| 国产999精品久久久| 欧美日韩国产一中文字不卡| 91高清免费视频| 最新国产精品拍自在线播放| 91免费精品视频| 国内精品久久久久久| 欧美精品性视频| 国产成人免费91av在线| 久热在线中文字幕色999舞| 国产精品久久99久久| 一区二区三区四区视频| 米奇精品一区二区三区在线观看| 国产亚洲欧美另类中文| 久久国产精品网站| 热re91久久精品国99热蜜臀| 中文字幕亚洲天堂|