日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android sdk build-tools_从零开始仿写一个抖音App——视频编辑SDK开发(一)

發(fā)布時間:2023/12/2 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android sdk build-tools_从零开始仿写一个抖音App——视频编辑SDK开发(一) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文首發(fā)于微信公眾號——世界上有意思的事,搬運轉(zhuǎn)載請注明出處,否則將追究版權(quán)責任。交流qq群:859640274。

大家好久不見,又有一個多月沒有發(fā)文章了。不知道還有哪些讀者記得我的?從零開始仿寫抖音App?的系列文章,這個系列的文章已經(jīng)很久沒有更新了,最后一篇文章是我開始開發(fā)?視頻編輯SDK?時寫的。當時踏入到了一個新的領(lǐng)域里,未知的東西太多了,導(dǎo)致接下來的大半年都沒有更新相關(guān)的文章。?但是別以為我已經(jīng)放棄了,今天對于我來說是一個值得紀念的日子,2019年10月28日?我終于將?視頻編輯SDK?的最簡版本給完成了,我將這個?視頻編輯SDK?命名為?WSVideoEditor,接下來的一段時間里我計劃更新 4 篇解析該 SDK 的相關(guān)文章,WsVideoEditor:https://github.com/TheGodsThemselves/WsVideoEditor 中的代碼我也會隨著文章同步更新。當 SDK 解析完畢之后?從零開始仿寫一個抖音App?系列文章將會踏出最關(guān)鍵的一步。公眾號后臺發(fā)送:視頻編輯SDK開發(fā)(一),可以獲得 PDF 文件。

本文分為以下章節(jié),讀者可按需閱讀:

  • 1.項目介紹

  • 2.SDK功能介紹

  • 3.SDK架構(gòu)以及運行機制介紹

  • 4.VideoDecodeService解析

一、項目介紹

本章我將介紹 WsVideoEditor:https://github.com/TheGodsThemselves/WsVideoEditor 項目的基本結(jié)構(gòu)、組織方式以及運行方式。需要大家把項目 clone 下來跟著我一步步來做。

1.基本結(jié)構(gòu)

我們看著圖1,一個個來講:

  • 1.android:顧名思義,這個目錄下是一個 Android 項目,去掉 .gradle、build、.idea 等等 ignore 的文件,我們主要關(guān)注下面這幾個文件夾。

?? ?

    • 1.ffmpeg-cpp:如圖2,這個文件夾中有 FFMPEG 的頭文件與 .so 文件,我們需要將這個庫集成到我們的 SDK 中,我們的?編輯SDK?需要有解碼視頻的能力,解碼分為硬解和軟解,ffmpeg 就是用于軟解的最強開源庫。至于如何得到這些東西,我之前寫過一篇 FFMPE食用指南:https://juejin.im/post/5c0d3c366fb9a049ea38c6b4 有興趣的讀者可以看看。

    • 2.protobuf-cpp:這個文件夾與 ffmpeg-cpp 類似,里面有 Protobuf For Cpp 的頭文件與 .a 文件,因為我們 Native 與 Android/iOS/Linux 的通信方式使用的是 Protobuf,所以我們也需要將 Cpp 層的 Protobuf 集成到我們的 SDK 中。

??

    • 3.wsvideoeditor-sdk:如圖3,這個文件夾是一個 Android Library 項目,我們的?編輯SDK?在 Android 端會以一個獨立的 jar 包形式存在。這個目錄下的東西比較多,例如 src 目錄下是 Java 層的一些封裝代碼。jni 目錄下是一些使用了 Android Native Api 的 Cpp 代碼。更詳細的解析,會在后面幾章。

    • 4.wsvideoeditor-test:這個文件夾則是一個 Android Application 項目,主要是用于編寫一些測試?編輯 SDK?的代碼。

  • 2.buildtools:如圖4,這個目錄下主要存放一些工具腳本,例如目前 build_proto.sh 用于生成 Java 與 Cpp 層的 Protobuf 代碼。

  • 3.ios、linux:因為我給?編輯SDK?的定義是一個跨平臺的視頻編輯SDK,所以未來的想法是 iOS 和 Linux 端也能接入我們的?編輯SDK,目前這兩個目錄里還啥也沒有:-D。?

  • 4.sharedcpp:如圖5,這個目錄里面主要存放與平臺無關(guān)的 Cpp 代碼,因為我們要做的是一個跨平臺的視頻編輯 SDK,所以盡量多的將與平臺無關(guān)的代碼進行共用,是一個明智的選擇。可以看見里面的 prebuilt_protobuf 目錄下就有我們使用 build_proto.sh 生成的 Cpp 文件,這些文件就是可以共用的。?

  • 5.sharedproto:這里存放著我們定義的 Protobuf 文件。

  • 6.thirdparty:這里存放著一些包含源碼的與平臺無關(guān)的三方庫,例如 libyuv。

  • 7.CMakeLists.txt:這個文件主要是為了讓 Clion 能夠識別我們這個整個項目。

2.如何運行項目

  • 1.git clone https://github.com/TheGodsThemselves/WsVideoEditor.git

  • 2.NDK 環(huán)境需要準備好

  • 3.用 Android Studio 打開?WsVideoEditor/android?目錄

  • 4.在手機中準備?/sdcard/test.mp4?視頻文件

  • 5.運行?wsvideoeditor-test?項目

二、SDK功能介紹

這一章我們來介紹一下?編輯SDK?目前有的以及未來會有的功能。編輯SDK?的最終形態(tài)會和抖音的視頻編輯功能接近,有其他想法的讀者也可以在評論區(qū)留言或者提 issue。

1.目前有的功能

  • 1.開始播放

  • 2.暫停播放

  • 3.視頻音量調(diào)整

  • 4.單段視頻播放

  • 5.多段視頻播放

  • 6.視頻 Seek

  • 7.視頻邊緣模糊填充

2.規(guī)劃中的功能

  • 1.視頻類:

    • 1.按時間軸添加額外的聲音

    • 2.按時間軸添加濾鏡

    • 3.按時間軸添加靜態(tài)貼紙、動態(tài)貼紙

    • 4.多段視頻間轉(zhuǎn)場

  • 2.圖片類:

    • 1.添加聲音

    • 2.多張圖片間的轉(zhuǎn)場

    • 3.照片電影

  • 3.工具類:

    • 1.視頻縮略圖截取

    • 2.視頻元數(shù)據(jù)讀取

  • 4.編碼類:

    • 1.導(dǎo)出不同格式的視頻

    • 2.更改視頻的分辨率、幀率

    • 3.視頻轉(zhuǎn) gif

  • 5.技術(shù)類:

    • 1.多進程編解碼視頻

    • 2.多進程播放視頻

    • 3.多進程視頻縮略圖截取

三、SDK架構(gòu)以及運行機制介紹

這一章我來介紹一下目前?編輯SDK?的整體架構(gòu)以及運行機制。

1.編輯SDK架構(gòu)

圖6是 編輯SDK 的架構(gòu)圖,這一節(jié)我會照著這張圖來介紹。

(1).基礎(chǔ)API

先從底部看起,底部是整個 SDK 依賴的底層 API 庫。

  • 1.FFMPEG:前面簡單介紹過,是一個開源的視頻庫,在我們的項目中主要用于軟編解碼

  • 2.MediaCodec:是 Android 中的硬編解碼?API,相應(yīng)的 iOS 也有自己的硬編解碼方式。

  • 3.OpenGL:是一個開源的圖形庫,Android 和 iOS 中都有內(nèi)置 OpenGL ES 作為默認圖形庫。在我們的項目中主要用于將視頻解碼后的視頻幀繪制到屏幕上去。當然也可以對這些圖像做一些效果的變化,例如濾鏡、視頻/圖片轉(zhuǎn)場等等。

  • 4.Libyuv:是 Google 開源的實現(xiàn)各種 YUV 與 RGB 之間相互轉(zhuǎn)換、旋轉(zhuǎn)、縮放的庫。

  • 5.Protobuf:是 Google 開源的一種平臺無關(guān)、語言無關(guān)、可擴展且輕便高效的序列化數(shù)據(jù)結(jié)構(gòu)的協(xié)議。在我們的項目中主要用于 Cpp 與 Java、OC、Dart 之間的數(shù)據(jù)通信。

(2).SDK主體

接著我們再看圖片中的主體部分,因為目前只有 Android 端的實現(xiàn),所以主體部分的上層實現(xiàn)我使用 Android 來代替。

  • 1.Android層架構(gòu)

    • 1.WSMediaPlayerView:繼承于 TextureView,所以其可以提供一個具有 Open GL 環(huán)境的線程。對 Surface 家族不了解的同學(xué)可以看看這兩篇文章:Android繪制機制以及Surface家族源碼全解析:https://juejin.im/post/5c666a9ff265da2d8b632bf9、相機/OpenGL/視頻/Flutter和SurfaceView:https://juejin.im/post/5d9fd2415188256bf067e23d#heading-5)

    • 2.WSMediaPlayer:這個是一個代理了 Native 的 NativeWSMediaPlayer 的 Java 類。該類具有一個播放器應(yīng)該有的各種 API,例如 play、pause、seek 等等。其實很多 Android 中的系統(tǒng)類都是以這種形式存在的,例如 Bitmap、Surface、Canvas 等等。說到底 Java 只是 Android 系統(tǒng)方便開發(fā)者開發(fā) App 的上層語言,系統(tǒng)中大部分的功能最終都會走到 Native 中去,所以讀者需要習(xí)慣這種代碼邏輯

    • 3.AudioPlayer:這個類是基于 Android 中的 AudioTrack 封裝的能夠播放音頻幀的 Java 類。我們在 Native 層也有一個 AudioPlayer。這里與 WSMediaPlayer 相反 Native 層的 AudioPlayer 是一個空殼,Java 層的 AudioPlayer 反向代理了 Native 層的 AudioPlayer,因為在這里 Java 層的 AudioPlayer 才是真正播放音頻的東西。

  • 2.Native層架構(gòu):這里我們自底向上來剖析,Native 層的架構(gòu)

    • 1.AudioDecodeService:它負責使用 FFMPEG/MediaCodec,來從視頻/音頻中解碼出某個時間點的音頻幀,并且存儲在一個音頻幀隊列中。最終被外部取出音頻幀交給音頻播放器播放。

    • 2.VideoDecodeService:它和 AudioDecodeService 類似,是使用 FFMPEG/MediaCodec 來從視頻中解碼出某個時間點的視頻幀并且存儲在一個視頻幀隊列中。最終被外部取出視頻幀交給 OpenGL 繪制到屏幕上。

    • 3.VideoFramePool:它負責響應(yīng)外部的 seek 事件,然后使用 FFMPEG/MediaCodec 來從視頻中解碼出當前時間點的視頻幀,然后存儲到一個 LruCache 中同時返回 seek 時間點的視頻幀。

    • 4.AudioPlayer:前面說過,這個是 Java 層的 AudioPlayer 代理類,主要用于播放 AudioDecodeService 解碼出來音頻幀。

    • 5.FrameRenderer:這個東西是一個渲染器,在視頻播放時用于渲染 VideoDecodeService 不斷解碼出的視頻幀,在視頻 seek 的時用于向 VideoDecoderPool 發(fā)送 seek 請求,然后渲染返回的視頻幀。

    • 6.NativeWSMediaPlayer:用于同步 AudioPlayer 和 FrameRenderer 的音視頻播放。即我們一般認為的視頻播放器實體,被 Java 層的 WSMediaPlayer 代理著。

2.編輯SDK運行機制

上一節(jié)講解了 編輯SDK 的架構(gòu),這一節(jié)在來基于圖7講講 編輯SDK 的運行機制。

  • 1.經(jīng)過上一節(jié)的介紹,我們都知道了 WSMediaPlayerView 是整個 編輯SDK 的頂級類。所以我們由 WSMediaPlayerView 入手,先看圖片最上面。

    • 1.可以看見 WSMediaPlayerView 中會維護一個?30ms的定時循環(huán),這個循環(huán)中會不斷的調(diào)用 draw frame 來驅(qū)動 WSMediaPlayer/NativeWSMediaPlayer 進行視頻/音頻的播放。

    • 2.與此同時,最左邊的用戶會通過 play、pause、seek 等 API 來更新 NativeWSMediaPlayer 的狀態(tài)。

    • 3.需要注意的是,WSMediaPlayerView 的定時循環(huán)不會被用戶的 play、pause、seek 等操作所中斷的。

  • 2.再來看看圖片左邊,這是 WSMediaPlayer 的內(nèi)部播放機制。要點為?三個循環(huán),兩個播放,我們還是自底向上解析。

    • 1.VideoDecodeService:它內(nèi)部維護了一個可阻塞循環(huán)與一個先進先出隊列——BlockingQueue,當我們開始播放視頻或者 seek 視頻到某個時間點的時候,VideoDecodeService 會記錄這個開始的時間點,然后不斷的解碼當前時間點之后的每一幀,每解碼出一幀便把這一幀放入 BlockingQueue 中。當隊列中的元素達到最大值時,當前的循環(huán)就會被阻塞,直到外部將 BlockingQueue 中的 Top 幀消費了,那么循環(huán)又會被啟動繼續(xù)解碼。需要注意的是:VideoDecodeService 只在視頻播放的時候提供視頻幀,因為在這個情況下 BlockingQueue 中的視頻幀的順序就是視頻真正播放的順序。

    • 2.VideoFramePool:它內(nèi)部維護了一個可阻塞請求循環(huán)與一個LruCachePool。一般情況下 VideoFramePool 的循環(huán)是處于阻塞狀態(tài)的。當外部 seek 視頻的時候,循環(huán)會接收到一個請求并開始處理這個請求,如果 LruCachePool 中有 Cache 被命中了,那么就直接返回 Cache,否則將會立即從視頻中解碼出這個請求中時間點的視頻幀存到 LruCachePool 中然后再返回。需要注意的是:VideoFramePool 只在視頻 seek 的時候提供視頻幀,因為我們的 seek 操作是隨機的,所以在這個情況下 VideoDecodeService 無法使用。

    • 3.AudioDecodeService:它與 VideoDecodeService 類似,也維護了一個可阻塞循環(huán)先進先出隊列,內(nèi)部的其他行為也類似,只是將視頻幀換成了音頻幀。

    • 4.FrameRenderer

      • 1.當視頻 seek 的時候,其會從 VideoFramePool 中取出 seek 時刻的視頻幀繪制它。

      • 2.當視頻處于 playing 狀態(tài)時,它的 drawFrame 方法就會不斷被 WSMediaPlayerView 通過定時循環(huán)調(diào)用并從 VideoDecodeService 中取出當前幀通過 Open GL 繪制它。

    • 5.AudioPlayer:當視頻處于 playing 狀態(tài)時,它也會不斷被 WSMediaPlayerView 通過定時循環(huán)驅(qū)動著從 AudioDecodeService 中取出當前的音頻幀,然后通過反向代理將音頻幀交給 Java 層的 AudioPlayer 進行播放。

四、VideoDecodeService解析

上一章大概的講了講整個?編輯SDK?的整體架構(gòu)和運行機制,但其實整個?編輯SDK?內(nèi)部的每一個部分的細節(jié)都非常多,所以這一章我會先講解 VideoDecodeService 的內(nèi)部細節(jié)。其他各個部分則放在后面幾篇文章中講解。與此同時,WsVideoEditor https://github.com/TheGodsThemselves/WsVideoEditor 中的代碼也會隨著講解的進行而不斷更新。最終形成一個可用的?編輯SDK

1.API講解

-----代碼塊1----- VideoDecodeService.javaprivate native long newNative(int bufferCapacity);private native void releaseNative(long nativeAddress);private native void setProjectNative(long nativeAddress, double startTime, byte[] projectData);private native void startNative(long nativeAddress);private native String getRenderFrameNative(long nativeAddress, double renderTime);private native void updateProjectNative(long nativeAddress, byte[] projectData);private native void seekNative(long nativeAddress, double seekTime);private native void stopNative(long nativeAddress);private native boolean endedNative(long nativeAddress);private native boolean stoppedNative(long nativeAddress);private native int getBufferedFrameCountNative(long nativeAddress);

如代碼塊1所示,我們先來講講 VideoDecodeService 的 API

  • 1.newNative:由前面幾章的講解我們知道,VideoDecoderService 內(nèi)部有一個先進先出的阻塞隊列,這個方法的入?yún)?bufferCapacity?就是用于設(shè)置這個阻塞隊列的長度。這個方法調(diào)用之后 Native 層會創(chuàng)建一個與 Java 層同名的 VideoDecodeService.cpp 對象。然后返回一個?long?表示這個 Cpp 對象的地址。我們會將其記錄在 Java 層,后續(xù)要調(diào)用其他方法時需要通過這個地址找到相應(yīng)的對象。

  • 2.releaseNative:因為 Cpp 沒有垃圾回收機制,所以 Cpp 對象都是需要手動釋放的,所以這個方法就是用于釋放 VideoDecodeService.cpp 對象。

  • 3.setProjectNative:因為 Protobuf 是高效的跨平臺通信協(xié)議,所以 Java 與 Cpp 層的通信方式使用的就是 Protobuf,我們可以看?ws_video_editor_sdk.proto?這個文件,里面定義的 EditorProject 就是兩端一起使用的數(shù)據(jù)結(jié)構(gòu)。這個方法的入?yún)?nativeAddress?就是我們在 1 中獲取到的對象地址。入?yún)?startTime?表示起始的解碼點,單位是秒。入?yún)?projectData?就是 EditorProject 序列化之后的字節(jié)流。

  • 4.startNative:這個方法表示開始解碼。

  • 5.getRenderFrameNative:這個方法表示獲取?renderTime?這一時刻的幀數(shù)據(jù),目前返回到 Java 層的是一個?String,在 Cpp 層后續(xù)我們主要就是使用這個方法獲取到的幀數(shù)據(jù)使用 OpenGL 繪制到屏幕上。?

  • 6.updateProjectNative:這個方法和?setProjectNative?類似,用于更新 EditorProject。

  • 7.seekNative:我們在看視頻的時候,將進度條拖動到某一時刻的操作被稱為 seek,在 VideoDecodeService 中的體現(xiàn)就是這個方法,這個方法會將當前的解碼時間點設(shè)置為?seekTime。

  • 8.stopNative:這個方法表示暫停解碼。

  • 9.endedNative:返回一個?boolean?表示視頻的解碼點是否到達了視頻的結(jié)尾。

  • 10.stoppedNative:返回了一個?boolean?表示當前是否暫停了解碼。

  • 11.getBufferedFrameCountNative:返回一個?int,表示當前阻塞隊列中有多少個幀,最大不會超過我們在 1 中設(shè)置的?bufferCapacity。?

2.代碼分析

這一小節(jié)中,我使用一個完整的例子來分析 VideoDecodeService 的源碼

  • 1.例子在 TestActivity 中,我們運行項目會看見界面上有三個 Button 和兩個 TextView。

  • 2.我們在?initButton?中進行了下面這些操作

    • 1.初始化了 ui。

    • 2.創(chuàng)建了一個 VideoDecodeService.java 類,內(nèi)部就是調(diào)用我們上一節(jié)說的?newNative方法。這個方法最終會進入到?video_decode_service.h?中調(diào)用 VideoDecodeService.cpp 的構(gòu)造方法,構(gòu)造方法則會創(chuàng)建一個 BlockingQueue.cpp 對象?decoded_unit_queue_,這就是我們一直說的?先進先出阻塞隊列

    • 3.構(gòu)建了一個 EditorProject.java,里面?zhèn)髁艘粋€需要解碼的視頻路徑?/sdcard/test.mp4

  • 3.我們點擊 START 按鈕

    • 1.stringBuilder?和?times?是用來記錄測試數(shù)據(jù)的就不說了

    • 2.這里然后調(diào)用了?setProject?方法,經(jīng)過一系列調(diào)用鏈后會通過 jni 進入到代碼塊3

      • 1.將?buffer?反序列化成 EditorProject.cpp 對象。

      • 2.address?強轉(zhuǎn) VideoDecodeService.cpp 對象。

      • 3.使用?LoadProject?方法解析出一些數(shù)據(jù),例如視頻的幀率、寬高等等。有興趣的讀者可以跟進入看看。

      • 4.調(diào)用?SetProject?給 VideoDecodeService.cpp 設(shè)置 EditorProject.cpp。

    • 3.調(diào)用?start?最終也是到代碼塊3中,調(diào)用?Start?方法。我們繼續(xù)進入?Start?方法中,發(fā)現(xiàn)其中是啟動了一個線程然后調(diào)用?VideoDecodeService::DecodeThreadMain,這個方法內(nèi)部則是一個?while?循環(huán),每當使用 FFMPEG 解碼出一個視頻幀的時候就會將這一幀放到?decoded_unit_queue_?中。當外部沒有消費者時,decoded_unit_queue_?的幀數(shù)量將會很快達到閾值(我們設(shè)置的是10),此時這個線程就會被阻塞。直到外部消費后,幀數(shù)量減少了,本線程將會繼續(xù)開始解碼視頻幀,如此往復(fù)。

-----代碼塊3----- com_whensunset_wsvideoeditorsdk_inner_VideoDecoderService.ccJNIEXPORT void JNICALLJava_com_whensunset_wsvideoeditorsdk_inner_VideoDecodeService_setProjectNative(JNIEnv *env, jobject, jlong address, jdouble render_pos, jbyteArray buffer) {VideoDecodeService *native_decode_service = reinterpret_cast<VideoDecodeService *>(address);model::EditorProject project;jbyte *buffer_elements = env->GetByteArrayElements(buffer, 0);project.ParseFromArray(buffer_elements, env->GetArrayLength(buffer));env->ReleaseByteArrayElements(buffer, buffer_elements, 0);LoadProject(&project);native_decode_service->SetProject(project, render_pos);}JNIEXPORT void JNICALL Java_com_whensunset_wsvideoeditorsdk_inner_VideoDecodeService_startNative(JNIEnv *, jobject, jlong address) {VideoDecodeService *native_decode_service = reinterpret_cast<VideoDecodeService *>(address);native_decode_service->Start();}
  • 4.繼續(xù)看代碼塊2,可以看見我啟動了一個 Java 層的無限循環(huán)線程,每隔 30ms 會 sleep 一下。每次循環(huán)我則會調(diào)用?getRenderFrame?方法來從 VideoDecodeService 中消費一個視頻幀。然后把幀的信息打印到 TextView 上面。其實這里的代碼可以類比為視頻的播放,VideoDecodeService 不斷地在后臺線程進行解碼按順序?qū)⒁曨l幀放入到隊列中,本線程則不斷的從隊列中取出一幀進行消費,就像視頻幀被渲染到屏幕上一樣。

  • 5.最下面還有一個 Java 層的無限循環(huán)線程,會不斷的讀取 VideoDecodeService 的其他信息打印到 TextView 上。

  • 6.這一節(jié)只是簡單的介紹 VideoDecodeService 的運行思路,其實代碼里還有很多實現(xiàn)細節(jié),這些細節(jié)解析就只能交給讀者了,畢竟我比較懶:-D

五、尾巴

終于從零開發(fā)仿寫一個抖音APP這一系列文章又重新開始更新了,今年以來文章的發(fā)表間隔長了很多,寫文章的時間也少了很多。但是為了那么多支持、關(guān)注我的讀者我也不能就這樣放棄更新。立一個 flag,今后每個月都要更新一篇文章,希望大家能夠多多支持,感謝!!

不販賣焦慮,也不標題黨。分享一些這個世界上有意思的事情。題材包括且不限于:科幻、科學(xué)、科技、互聯(lián)網(wǎng)、程序員、計算機編程。下面是我的微信公眾號:世界上有意思的事,干貨多多等你來看。

總結(jié)

以上是生活随笔為你收集整理的android sdk build-tools_从零开始仿写一个抖音App——视频编辑SDK开发(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 91看大片 | 91精品国产一区二区 | 小日子的在线观看免费第8集 | 少妇太紧太爽又黄又硬又爽小说 | 日剧网 | 久久人人爽爽 | 小婕子伦流澡到高潮h | 欧美最顶级a∨艳星 | 人妖性做爰aaaa | 黄色成人av网站 | 亚洲在线一区二区三区 | 在线sese | 国产在线精品一区二区 | 欧美在线网 | 四虎成人免费视频 | 亚洲第一中文字幕 | 91高跟黑色丝袜呻吟动态图 | 亚洲高清视频在线 | 16—17女人毛片 | 99在线免费 | 国产激情av | 黄色免费看片 | 日一区二区三区 | 色欲人妻综合网 | 成人精品网址 | 亚洲操操 | 高潮网址 | 国精品一区二区三区 | 日本成人在线网站 | 国产女人爽到高潮a毛片 | 亚洲啪啪免费视频 | 北条麻妃一区二区三区四区五区 | 国产成人午夜高潮毛片 | 久草影视网| 香蕉视频免费在线播放 | 91精品久久久久久久 | 色偷偷资源网 | 国产无遮挡免费视频 | 国产综合视频一区 | 欧美国产日韩在线视频 | 国产精品理论片 | 四川丰满妇女毛片四川话 | 护士人妻hd中文字幕 | 日韩av一区在线观看 | 一边摸内裤一边吻胸 | 亚洲国产精品一区 | 亚洲激情精品 | 午夜影院在线观看免费 | 久久激情网 | 欧美日韩欧美日韩在线观看视频 | 51人人看 | 台湾佬美性中文娱乐网 | 青青草原综合久久大伊人精品 | 麻豆视频在线观看免费网站黄 | 久久国产一 | 久久精品免费在线观看 | 动漫大乳美女 | 福利第一页 | 黄页视频在线免费观看 | 富二代成人短视频 | 全黄一级男人和女人 | www.性欧美 | 欧美人妻少妇一区二区三区 | 亚av | 久久久亚洲精品无码 | 亚洲成人福利 | 国产精品男同 | 97人人精品 | 久久久久女 | 中文字幕亚洲欧美日韩在线不卡 | 国产一区二区精品在线 | 欧美久久久久久久久久久 | 欧洲美女与动交zozzo | 丝袜美女被c | 97精品一区 | 青青青视频在线播放 | 国产精品久久久久毛片大屁完整版 | 国产三级精品三级 | a天堂中文网 | 亚州欧美日韩 | 九九九热视频 | 香蕉视频在线观看网站 | 日本天堂网 | 男女激情四射网站 | 美女洗澡无遮挡 | jizz在线免费观看 | 欧美精品一级在线观看 | 黄色福利视频网站 | 成人区人妻精品一熟女 | 青草青草久热 | 一级视频免费观看 | 国产一区日韩 | 国产精品有码 | 36d大奶| 欧美在线观看一区 | 欧美一区二区三区在线观看 | 在线观看一二三区 | 国产精品自产拍 | 日韩精品一区二区三区网站 |