安卓視頻播放(阿里云視頻點(diǎn)播播放器SDK+SurfaceView)

本篇文章講述使用阿里云視頻視頻播放sdk中的高級(jí)播放器加上SurfaceView實(shí)現(xiàn)四敞,采用id+STS方法進(jìn)行視頻播放。

目錄

  • 概念介紹
  • STS的播放流程
  • 如何導(dǎo)入
  • 自定義SurfaceView
  • 橫豎屏切換
  • 滑動(dòng)快進(jìn)拔妥,后退
  • 滑動(dòng)調(diào)節(jié)音量
  • 滑動(dòng)調(diào)節(jié)亮度
  • 按Home鍵后重新點(diǎn)開黑屏問(wèn)題
  • Token過(guò)期問(wèn)題

一 首先是概念介紹(只介紹用到的)

  1. 高級(jí)播放器:除了具備基礎(chǔ)播放器的所有功能外忿危,還提供播放視頻的高級(jí)能力,如視頻加密没龙、安全下載铺厨、邊播邊下緩存、清晰度切換等功能硬纤,建議使用阿里視頻云點(diǎn)播和直播業(yè)務(wù)的用戶使用解滓。
  2. RAM和STS:RAM和STS是阿里云提供的權(quán)限管理系統(tǒng)。RAM主要的作用是控制賬號(hào)系統(tǒng)的權(quán)限筝家。通過(guò)使用RAM可以將在主賬號(hào)的權(quán)限范圍內(nèi)創(chuàng)建子用戶洼裤,給不同的子用戶分配不同的權(quán)限從而達(dá)到授權(quán)管理的目的。STS是一個(gè)安全憑證(Token)的管理系統(tǒng)肛鹏,用來(lái)授予臨時(shí)的訪問(wèn)權(quán)限逸邦,這樣就可以通過(guò)STS來(lái)完成對(duì)于臨時(shí)用戶的訪問(wèn)授權(quán)。

二 STS的播放流程

流程:用戶App獲取STS憑證 -> 服務(wù)端下發(fā)STS憑證 -> 用戶上傳視頻并獲取vid -> 服務(wù)端獲取STS憑證 -> 將STS憑證下發(fā)給客戶端 -> 完成視頻播放在扰。


TIM圖片20180327175524.png

三 如何導(dǎo)入

請(qǐng)看阿里云文檔=========》》》》》》阿里云-高級(jí)播放器Android使用說(shuō)明

四 自定義SurfaceView

  • 為什么不直接用SurfaceView而是自定義缕减?
    如果想實(shí)現(xiàn)觸摸滑動(dòng)事件,就必須自定義芒珠。
    自定義SurfaceView需要繼承SurfaceView桥狡,我們現(xiàn)在的需求是滑動(dòng)觸控。
羅列出這幾種情況:
  1. 亮度手勢(shì)皱卓,手指在SurfaceView左半部上下滑動(dòng)時(shí)候調(diào)用
    2.音量手勢(shì)裹芝,手指在SurfaceView右半部上下滑動(dòng)時(shí)候調(diào)用
    3.快進(jìn)快退手勢(shì),手指在SurfaceView左右滑動(dòng)的時(shí)候調(diào)用
    4.按下手勢(shì)娜汁,第一根手指按下時(shí)候調(diào)用
    5.快進(jìn)快退執(zhí)行后的松開時(shí)候調(diào)用
    我就這幾種情況創(chuàng)建了一個(gè)接口 :VideoGesureListener
public interface VideoGesureListener{
        //亮度手勢(shì)嫂易,手指在SurfaceView左半部上下滑動(dòng)時(shí)候調(diào)用
        public void onBrightnessGesture(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);

        //音量手勢(shì),手指在SurfaceView右半部上下滑動(dòng)時(shí)候調(diào)用
        public void onVolumeGesture(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);

        //快進(jìn)快退手勢(shì)掐禁,手指在SurfaceView左右滑動(dòng)的時(shí)候調(diào)用
        public void onFF_REWGesture(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);

        //按下手勢(shì)怜械,第一根手指按下時(shí)候調(diào)用
        public void onDown(MotionEvent e);

        //快進(jìn)快退執(zhí)行后的松開時(shí)候調(diào)用
        public void onEndFF_REW(MotionEvent e);
    }

接下來(lái)我們來(lái)看一下安卓給我們提供的手勢(shì)控制類

GestureDetector

接口

  • OnGestureListener:監(jiān)聽一些手勢(shì),如單擊傅事、滑動(dòng)缕允、長(zhǎng)按等操作
    1.onDown(MotionEvent e):按下屏幕的時(shí)候回調(diào)
    2.onShowPress(MotionEvent e):按下后100ms內(nèi)未松開的時(shí)候回調(diào)(目前不知有何用)
    3.onLongPress(MotionEvent e):用戶長(zhǎng)按后回調(diào)
    4.onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY):手指滑動(dòng)的時(shí)候回調(diào),e1,e2分別是之前DOWN事件和當(dāng)前的MOVE事件蹭越,distanceX和distanceY就是當(dāng)前MOVE事件和上一個(gè)MOVE事件的位移量
    5.onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY):滑動(dòng)后慣性操作回調(diào)
    6.onSingleTapUp(MotionEvent e):點(diǎn)擊抬起時(shí)間障本,沒(méi)有長(zhǎng)按和滑動(dòng)時(shí)回調(diào)
  • OnDoubleTapListener:監(jiān)聽雙擊和單擊事件。
    1.onSingleTapConfirmed(MotionEvent e):?jiǎn)螕羰录卣{(diào)
    2.onDoubleTap(MotionEvent e):雙擊事件回調(diào)
    3.onDoubleTapEvent(MotionEvent e):onDoubleTap雙擊后的回調(diào)
  • OnContextClickListener:監(jiān)聽鼠標(biāo)右鍵
    1.onContextClick(MotionEvent e):當(dāng)鼠標(biāo)/觸摸板,右鍵點(diǎn)擊時(shí)候的回調(diào)驾霜。
    內(nèi)部類
  • SimpleOnGestureListener:實(shí)現(xiàn)上面三個(gè)接口的內(nèi)部類案训,擁有上面三個(gè)的所有回調(diào)
    因?yàn)镾impleOnGestureListener不是抽象類,所以我們一般應(yīng)它進(jìn)行操縱粪糙,這樣我們就可以只寫我們用到的方法萤衰,就不用全部重寫所有的方法。
GestureDetector的使用與需要結(jié)合觸摸事件猜旬。只有感受到觸摸事件才能去進(jìn)行手指觸控脆栋。

setOnTouchListener(this);

 @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction()==MotionEvent.ACTION_UP){
            if (hasFF_REW){
               if (mVideoGestureListener!=null){
                    mVideoGestureListener.onEndFF_REW(event);
               }
                hasFF_REW=false;
            }
        }
        return mGestureDetector.onTouchEvent(event);
    }

其中定義了四種狀態(tài) NONE = 0, VOLUME = 1, BRIGHTNESS = 2, FF_REW = 3;
接下來(lái)我們來(lái)看一下我們自定義的SurfaceViewOnGestureListener繼承 GestureDetector.SimpleOnGestureListener主要用到了
onDown(MotionEvent e)
onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)兩個(gè)方法

 public class SurfaceViewOnGestureListener extends GestureDetector.SimpleOnGestureListener{
       private  MySurfaceView mySurfaceView;
       public SurfaceViewOnGestureListener(MySurfaceView mySurfaceView) {
           this.mySurfaceView =mySurfaceView;
       }

   A:    @Override
       public boolean onDown(MotionEvent e) {
           hasFF_REW=false;
           mScrollMode=NONE;
           if (mVideoGestureListener!=null){
               mVideoGestureListener.onDown(e);
           }
           return true;
       }

       @Override
       public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
           switch (mScrollMode){
B:
               case NONE:
                   if (Math.abs(distanceX)-Math.abs(distanceY)>offsetX){
                       mScrollMode=FF_REW;
                   }else {
C:
                       if (e1.getX()<getWidth()/2){
                           mScrollMode=BRIGHTNESS;
                       }else {
                           mScrollMode=VOLUME;
                       }
                   }
                   break;
               case VOLUME:
                   if (mVideoGestureListener!=null){
                       mVideoGestureListener.onVolumeGesture(e1,e2,distanceX,distanceY);
                   }
                   break;
               case BRIGHTNESS:
                   if (mVideoGestureListener!=null){
                       mVideoGestureListener.onBrightnessGesture(e1,e2,distanceX,distanceY);
                   }
                   break;
               case FF_REW:
                   if (mVideoGestureListener!=null){
                       mVideoGestureListener.onFF_REWGesture(e1,e2,distanceX,distanceY);
                   }
                   hasFF_REW = true;

                   break;
           }
           return true;
       }

a.在onDown的時(shí)候把狀態(tài)設(shè)置NONE
b.判斷橫向滑動(dòng)的距離大于縱向滑動(dòng)的距離,就把模式賦值為快進(jìn)和后退
c.在onScroll中進(jìn)行狀態(tài)賦值洒擦,根據(jù)滑動(dòng)的距離椿争,如果按下的點(diǎn)在屏幕的左半部分就吧狀態(tài)設(shè)置為調(diào)節(jié)亮度BRIGHTNESS,如果在右半部分就是調(diào)節(jié)音量
d.各種情況調(diào)用各自的接口方法

五 橫豎屏切換

  1. 不設(shè)置Activity的android:configChanges,或著設(shè)置Activity的android:configChanges="orientation"熟嫩,再或著設(shè)置Activity的android:configChanges="orientation|keyboardHidden"秦踪,切屏?xí)匦抡{(diào)用各個(gè)生命周期,切橫屏?xí)r會(huì)執(zhí)行一次掸茅,切豎屏?xí)r會(huì)執(zhí)行一次椅邓。
  2. 配置 android:configChanges="orientation|keyboardHidden|screenSize",不銷毀 activity昧狮,只調(diào)用 onConfigurationChanged方法景馁。
  3. 首先我們要先判斷當(dāng)前的屏幕是橫屏還是豎屏getResources().getConfiguration().orientation (獲取狀態(tài))進(jìn)行比較
    Configuration.ORIENTATION_LANDSCAPE(橫屏)
    Configuration.ORIENTATION_PORTRAIT(豎屏)
    然后用 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);(setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);)進(jìn)行橫豎屏設(shè)置
    接下來(lái)我們需要在onConfigurationChanged(Configuration newConfig)方法里面進(jìn)行設(shè)置屏幕切換后大小即可
  //獲得屏幕寬高
        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        width = displayMetrics.widthPixels;
        height = displayMetrics.heightPixels;

        //設(shè)置寬高,首先要找到外部包裹視頻界面的布局
        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mySurfaceView.getLayoutParams();
        params.width = width;
        if (newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
            params.height = height;
        }
        if (newConfig.orientation==Configuration.ORIENTATION_PORTRAIT){
            params.height = (width /16)*9;(正常比例)
        }
        mySurfaceView.setLayoutParams(params);

六 滑動(dòng)快進(jìn)逗鸣,后退

快進(jìn)和后退合住,我們需要知道的就是我們滑動(dòng)的距離如何與視頻的長(zhǎng)度關(guān)聯(lián)起來(lái)。
那么咱們就可以把視頻的總長(zhǎng)度與屏幕的總長(zhǎng)度相比撒璧,這樣就能知道你手指滑動(dòng)的距離占視頻的多少了透葛。
我們可以通過(guò) l = duration / mySurfaceView.getWidth();來(lái)獲得這個(gè)比例,然后用當(dāng)前的進(jìn)度加上指滑動(dòng)的距離占視頻的長(zhǎng)度就是要播放的視頻位置

 float offset = e2.getX() - e1.getX();
        if (offset>0){
            float v = currentPosition + (offset * l) / 4;(除以4是為了讓快進(jìn)的量不是特別大)
            aliyunVodPlayer.seekTo((int) v);
        }else {
            float v = currentPosition +(offset * l) / 4;
            aliyunVodPlayer.seekTo((int) v);
        }
    }

抬起點(diǎn)的x坐標(biāo)與按下點(diǎn)的X坐標(biāo)所得的距離卿樱,大于0是快進(jìn)僚害,小于零是后退。

七 滑動(dòng)調(diào)節(jié)音量

系統(tǒng)的音量有很多繁调,包括通話音量值萨蚕,系統(tǒng)鈴聲值,音樂(lè)音量值涉馁,鬧鈴音量值门岔,等等吧爱致。
做一下筆記以備以后用到

 AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
  max = am.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);// 0
  current= am.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
  • AudioManager.STREAM_VOICE_CALL :通話音量值
  • AudioManager.STREAM_SYSTEM:系統(tǒng)音量值
  • AudioManager.STREAM_RING:系統(tǒng)鈴聲值
  • AudioManager.STREAM_MUSIC:音樂(lè)音量值
  • AudioManager.STREAM_ALARM:鬧鈴音量值
  • AudioManager.STREAM_NOTIFICATION:提示聲音音量值

視頻播放我們用的是音樂(lè)音量值烤送,同樣的道理,我們需要把音量和高度進(jìn)行關(guān)聯(lián)糠悯,我們可以控件的高度閉上最大音量得出比例后就可以知道你滑動(dòng)的距離占音量的多少了帮坚。

  int value =  mySurfaceView.getHeight()/maxVolume ;
        int newVolume = (int) ((e1.getY()-e2.getY())/value+oldVolume);
        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,newVolume,AudioManager.FLAG_PLAY_SOUND);

八 滑動(dòng)調(diào)節(jié)亮度

1. 屏幕亮度的調(diào)節(jié)模式有兩種
  • Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC:值為1妻往,自動(dòng)調(diào)節(jié)亮度。
  • Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL:值為0试和,手動(dòng)模式讯泣。
2. 屏幕最大亮度為255, 屏幕最低亮度為0阅悍, 屏幕亮度值范圍必須位于:0~255好渠。
3. 在設(shè)置系統(tǒng)屏幕亮度前,需要保證AndroidManifest.xml中聲明如下權(quán)限(6.0需要?jiǎng)討B(tài)設(shè)置):<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
設(shè)置屏幕亮度有兩種方式
  • 一種是通過(guò)WindowManager去設(shè)置當(dāng)前界面的亮度
WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
lp.screenBrightness = Float.valueOf(brightness) ;
activity.getWindow().setAttributes(lp);

這里有個(gè)注意點(diǎn)就是activity是當(dāng)前的這個(gè)界面节视,設(shè)置的是當(dāng)前的界面拳锚,離開這個(gè)界面后就不管用了。

  • 第二種方式時(shí)通過(guò)修改系統(tǒng)數(shù)據(jù)庫(kù)來(lái)設(shè)置亮度
ContentResolver contentResolver = context.getContentResolver()寻行;
try {
            if(Settings.System.getInt(contentResolver,Settings.System.SCREEN_BRIGHTNESS_MODE)== Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC){
                Settings.System.putInt(contentResolver,Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
            }
        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
        }
        Settings.System.putInt(contentResolver, Settings.System.SCREEN_BRIGHTNESS,newBrightness);

相同的道理和滑動(dòng)調(diào)節(jié)音量一樣也是獲得屏幕的高度比上最大的亮度霍掺,然后計(jì)算滑動(dòng)的距離轉(zhuǎn)換成亮度是多少。(這里不多講了)

九 按Home鍵后重新點(diǎn)開黑屏問(wèn)題

為什么會(huì)出現(xiàn)黑屏拌蜘,就是按Home鍵再點(diǎn)App回來(lái)后杆烁,只有聲音沒(méi)有圖片的問(wèn)題,因?yàn)槲覀冇玫氖荢urfaceView简卧,每次點(diǎn)擊Home鍵時(shí)會(huì)銷毀這個(gè)SurfaceView兔魂,再回來(lái)時(shí)又會(huì)重新創(chuàng)建,這樣我們的阿里云播放器與SurfaceView就沒(méi)有綁定了举娩,畫面就沒(méi)有了入热。
這樣我們需要 給surfaceView添加mySurfaceView.getHolder().addCallback(this);

 @Override
    public void surfaceCreated(SurfaceHolder holder) {

        if (des==true){
            aliyunVodPlayer.setSurface(mySurfaceView.getHolder().getSurface());
            aliyunVodPlayer.resume();

        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

        aliyunVodPlayer.pause();

        des = true;
    }

我們?cè)诎磆ome鍵的時(shí)候會(huì)走surfaceDestroyed。這樣晓铆,我們就可以在這里做一個(gè)標(biāo)識(shí)勺良,讓他暫停,然后再回來(lái)的時(shí)候就會(huì)走surfaceCreated骄噪,判斷標(biāo)識(shí)尚困,然后進(jìn)行處理就可以了。切記一定要重新讓aliyunVodPlayer與SurfaceView進(jìn)行關(guān)聯(lián)链蕊,這樣才能有畫面也有聲音事甜。

十 Token過(guò)期問(wèn)題

由于我們的視頻在阿里云的服務(wù)器上存著,訪問(wèn)阿里云的服務(wù)器需要臨時(shí)憑證滔韵,我們通過(guò)STS來(lái)獲取Token逻谦,但是這個(gè)Token是有時(shí)間限制,正好阿里云的播放器給我們提供了播放視頻出錯(cuò)時(shí)候的回調(diào)接口陪蜻,我們只需要在這里面進(jìn)行重新請(qǐng)求Token就可以了

  aliyunVodPlayer.setOnErrorListener(new IAliyunVodPlayer.OnErrorListener() {
            @Override
            public void onError(int arg0, int arg1, String msg) {
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末邦马,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌滋将,老刑警劉巖邻悬,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異随闽,居然都是意外死亡父丰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門掘宪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蛾扇,“玉大人,你說(shuō)我怎么就攤上這事魏滚∑ㄉ#” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵栏赴,是天一觀的道長(zhǎng)蘑斧。 經(jīng)常有香客問(wèn)我,道長(zhǎng)须眷,這世上最難降的妖魔是什么竖瘾? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮花颗,結(jié)果婚禮上捕传,老公的妹妹穿的比我還像新娘。我一直安慰自己扩劝,他們只是感情好庸论,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棒呛,像睡著了一般聂示。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上簇秒,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天鱼喉,我揣著相機(jī)與錄音,去河邊找鬼趋观。 笑死扛禽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的皱坛。 我是一名探鬼主播编曼,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼剩辟!你這毒婦竟也來(lái)了掐场?” 一聲冷哼從身側(cè)響起往扔,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刻肄,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體融欧,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敏弃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了噪馏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片麦到。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖欠肾,靈堂內(nèi)的尸體忽然破棺而出瓶颠,到底是詐尸還是另有隱情,我是刑警寧澤刺桃,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布粹淋,位于F島的核電站,受9級(jí)特大地震影響瑟慈,放射性物質(zhì)發(fā)生泄漏桃移。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一葛碧、第九天 我趴在偏房一處隱蔽的房頂上張望借杰。 院中可真熱鬧,春花似錦进泼、人聲如沸蔗衡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)绞惦。三九已至,卻和暖如春洋措,著一層夾襖步出監(jiān)牢的瞬間翩隧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工呻纹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留堆生,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓雷酪,卻偏偏與公主長(zhǎng)得像淑仆,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哥力,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容