利用ffmpeg和opencv進(jìn)行視頻的解碼播放

引子

OpenCV中有自己的用于處理圖片和視頻的類VideoCapture刹缝,可以很方便的讀入文件和顯示。
現(xiàn)在視頻數(shù)據(jù)流是ffmpeg解碼h264文件得到的颈将,由于要依賴該數(shù)據(jù)源進(jìn)行相應(yīng)的后續(xù)處理梢夯,所以需要將ffmpeg中得到的數(shù)據(jù)緩存轉(zhuǎn)換成可以被OpenCV處理的Mat類對象。

ffmpeg介紹

FFmpeg是一個(gè)開源免費(fèi)跨平臺的視頻和音頻流方案晴圾,屬于自由軟件颂砸,采用LGPL或GPL許可證(依據(jù)你選擇的組件)。它提供了錄制死姚、轉(zhuǎn)換以及流化音視頻的完整解決方案人乓。它包含了非常先進(jìn)的音頻/視頻編解碼庫libavcodec,為了保證高可移植性和編解碼質(zhì)量都毒,libavcodec里很多codec都是從頭開發(fā)的色罚。
FFmpeg是一套可以用來記錄、轉(zhuǎn)換數(shù)字音頻账劲、視頻戳护,并能將其轉(zhuǎn)化為流的開源計(jì)算機(jī)程序。它包括了目前領(lǐng)先的音/視頻編碼庫libavcodec涤垫。 FFmpeg是在Linux下開發(fā)出來的姑尺,但它可以在包括Windows在內(nèi)的大多數(shù)操作系統(tǒng)中編譯。

FFmpeg的組成結(jié)構(gòu)

FFmpeg主要由一下幾個(gè)部分組成:

  • libavcodec:一個(gè)包含了所有FFmpeg音視頻編解碼器的庫蝠猬。 為了保證最優(yōu)性能和高可復(fù)用性,大多數(shù)編解碼器從頭開發(fā)的切蟋。
  • libavformat:一個(gè)包含了所有的普通音視格式的解析器和 產(chǎn)生器的庫。
  • 三個(gè)實(shí)例程序榆芦,這三個(gè)實(shí)例較為復(fù)雜柄粹,基本可以作為API使用手冊:
    ffmpeg:命令行的視頻格式轉(zhuǎn)換程序喘鸟。
    ffplay:視頻播放程序。(需要SDL支持)
    ffserver:多媒體服務(wù)器

了解完組成結(jié)構(gòu)后驻右,你會發(fā)現(xiàn)什黑,如果你在尋找一種視頻格式轉(zhuǎn)換的方式,那FFmpeg絕對是你的第一選擇堪夭,libavcodec 則又是重 中之重愕把。如果遇上API不會使用的情況,可以參考ffmpeg.c森爽、ffplay.c恨豁、 ffserver.c、apiexample.c(解碼)和output_example.c(編碼)爬迟。

ffmpeg使用說明

ffmpeg庫的接口都是c函數(shù)橘蜜,其頭文件也沒有extern "C"的聲明,所以在cpp文件里調(diào)用ffmpeg函數(shù)要注意了付呕。
一般來說计福,一個(gè)用C寫成的庫如果想被C/C++同時(shí)可以使用,那在頭文件應(yīng)該加上

#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
} // endof extern "C"
#endif

如果在.cpp里調(diào)用av_register_all()在鏈接時(shí)將找到不符號徽职,因?yàn)?cpp要求的符號名
和ffmpeg庫提供的符號名不一致象颖。
可以這么解決:

extern "C"
{
#include <libavutil/avutil.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}

使用ffmpeg SDK解碼流數(shù)據(jù)過程

以H264視頻流為例,講解解碼流數(shù)據(jù)的步驟姆钉。

準(zhǔn)備變量

定義AVCodec力麸,AVCodec *變量為解碼器指針。
定義AVCodecContext育韩,使用該變量可以將其定義為ffmpeg解碼類的類成員。
定義AVFrame闺鲸,AVFrame描述一個(gè)多媒體幀筋讨。解碼后的數(shù)據(jù)將被放在其中。
定義AVFormatContext變量摸恍,AVFormatContext用于保存視頻流的有效信息悉罕。

AVCodec *pCodec;
AVCodecContext * pCodecCtx;
AVFrame * pAvFrame;
AVFormatContext *pFormatCtx;

初始化解碼器

第一件事情就是初始化libavformat/libavcodec:
ffmpeg注冊復(fù)用器,編碼器等的函數(shù)av_register_all()立镶。

av_register_all();

這一步注冊庫中含有的所有可用的文件格式和編碼器壁袄,這樣當(dāng)打開一個(gè)文件時(shí),它們才能夠自動(dòng)選擇相應(yīng)的文件格式和編碼器媚媒。要注意你只需調(diào)用一次 av_register_all()嗜逻,所以,盡可能的在你的初始代碼中使用它缭召。這里注冊了所有的文件格式和編解碼器的庫栈顷,所以它們將被自動(dòng)的使用在被打開的合適格式的文件上逆日。注意你只需要調(diào)用 av_register_all()一次,因此我們在主函數(shù)main()中來調(diào)用它萄凤。如果你喜歡室抽,也可以只注冊特定的格式和編解碼器,但是通常你沒有必要這樣做靡努。

打開視頻文件坪圾,取出包含在文件中的流信息

// 打開視頻文件
if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0)
       handle_error(); // 不能打開此文件

這個(gè)函數(shù)讀取文件的頭部并且把信息保存到我們給的AVFormatContext結(jié)構(gòu)體中。
最后三個(gè)參數(shù)描述了文件格式惑朦,緩沖區(qū)大惺扌埂(size)和格式參數(shù);我們通過簡單地指明NULL或0告訴 libavformat 去自動(dòng)探測文件格式并且使用默認(rèn)的緩沖區(qū)大小行嗤。

// 取出流信息
if(av_find_stream_info(pFormatCtx)<0)
     handle_error(); // 不能夠找到流信息

查找文件的流信息,avformat_open_input函數(shù)只是檢測了文件的頭部已日,接著要檢查在文件中的流的信息。
這一步會用有效的信息把 AVFormatContext 的流域(streams field)填滿栅屏。作為一個(gè)可調(diào)試的診斷飘千,我們會將這些信息全盤輸出到標(biāo)準(zhǔn)錯(cuò)誤輸出中,不過你在一個(gè)應(yīng)用程序的產(chǎn)品中并不用這么做栈雳。

我們僅僅處理視頻流护奈,而不是音頻流。為了讓這件事情更容易理解哥纫,我們只簡單使用我們發(fā)現(xiàn)的第一種視頻流霉旗。

//遍歷文件的各個(gè)流,找到第一個(gè)視頻流蛀骇,并記錄該流的編碼信息
videoindex = -1;
for(i=0; i<pFormatCtx->nb_streams; i++) 
{
    if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
    {
        videoindex=i;
        break;
    }
}
if(videoindex==-1)
{
    printf("Didn't find a video stream.\n");
    return;
}
pCodecCtx=pFormatCtx->streams[videoindex]->codec;

我們已經(jīng)得到了一個(gè)指向視頻流的稱之為上下文的指針厌秒。接下來,我們需要找到真正的編碼器打開它擅憔。

尋找視頻流的解碼器

在庫里面查找支持該格式的解碼器

pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec == NULL)
    handle_error(); // 找不到解碼器

打開解碼器

if(avcodec_open(pCodecCtx, pCodec)<0)
    handle_error();

給視頻幀分配空間鸵闪,以便存儲解碼后的圖片數(shù)據(jù)

pAvFrame = avcodec_alloc_frame();

解碼視頻幀就像我前面提到過的,視頻文件包含數(shù)個(gè)音頻和視頻流暑诸,并且他們各個(gè)獨(dú)自被分開存儲在固定大小的包里蚌讼。我們要做的就是使用libavformat依次讀取這些包,過濾掉所有那些視頻流中我們不感興趣的部分个榕,并把它們交給libavcodec進(jìn)行解碼處理篡石。

進(jìn)行解碼

通過該api讀入一幀

result = av_read_frame(pFormatCtx, packet);

通過下面的api進(jìn)行解碼一幀數(shù)據(jù),將有效的圖像數(shù)據(jù)存儲到pAvFrame成員變量中

ret = avcodec_decode_video2(pCodecCtx, pAvFrame, &got_picture, packet);

下面是ffmpeg解碼的API:


將YUV420p顏色編碼轉(zhuǎn)換成BGR顏色編碼

首先得到圖片轉(zhuǎn)換上下文img_convert_ctx,這里注意的是西采,opencv的RGB編碼順序?yàn)锽GR凰萨,所以選用AV_PIX_FMT_BGR24的編碼方式。

//根據(jù)編碼信息設(shè)置渲染格式
            if(img_convert_ctx == NULL){
                img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
                    pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
                    AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL); 
            }   

再得到為BGR格式幀分配內(nèi)存

AVFrame *pFrameRGB = NULL;
uint8_t  *out_bufferRGB = NULL;
pFrameRGB = avcodec_alloc_frame();

//給pFrameRGB幀加上分配的內(nèi)存;
int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
out_bufferRGB = new uint8_t[size];
avpicture_fill((AVPicture *)pFrameRGB, out_bufferRGB, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);

最后進(jìn)行轉(zhuǎn)換

sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

OpenCV Mat數(shù)據(jù)復(fù)制

cv::Mat對象中有data指針,指向內(nèi)存中存放矩陣數(shù)據(jù)的一塊內(nèi)存 (uchar data)沟蔑。*
所以湿诊,要將ffmpeg解碼之后得到的RGB色彩的幀數(shù)據(jù)復(fù)制給該指針,這樣就實(shí)現(xiàn)了ffmpeg解碼數(shù)據(jù)到opencv中Mat格式的轉(zhuǎn)換瘦材,進(jìn)而就可以對Mat對象進(jìn)行相應(yīng)的處理厅须。

代碼示例

ffmpegDecode.h文件

#ifndef __FFMPEG_DECODE_H__
#define __FFMPEG_DECODE_H__

#include <opencv2/core/core.hpp>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
//圖像轉(zhuǎn)換結(jié)構(gòu)需要引入的頭文件
#include "libswscale/swscale.h"
};


#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib  ")
#pragma comment(lib, "avutil.lib    ")
#pragma comment(lib, "avdevice.lib  ")
#pragma comment(lib, "avfilter.lib  ")
#pragma comment(lib, "postproc.lib  ")
#pragma comment(lib, "swresample.lib")
#pragma comment(lib, "swscale.lib   ")

class ffmpegDecode
{
public:
    ffmpegDecode(char * file = NULL);
    ~ffmpegDecode();

    cv::Mat getDecodedFrame();
    cv::Mat getLastFrame();
    int readOneFrame();
    int getFrameInterval();

private:
    AVFrame *pAvFrame;
    AVFormatContext *pFormatCtx;
    AVCodecContext  *pCodecCtx;
    AVCodec         *pCodec;

    int i; 
    int videoindex;
    
    char *filepath;
    int ret, got_picture;
    SwsContext *img_convert_ctx;
    int y_size;
    AVPacket *packet;

    cv::Mat *pCvMat;

    void init();
    void openDecode();
    void prepare();
    void get(AVCodecContext *pCodecCtx, SwsContext *img_convert_ctx,AVFrame *pFrame);
};

#endif

ffmpegDecode.cpp文件

#include "ffmpegDecode.h"

ffmpegDecode :: ~ffmpegDecode()
{
    pCvMat->release();
    //釋放本次讀取的幀內(nèi)存
    av_free_packet(packet);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
}

ffmpegDecode :: ffmpegDecode(char * file)
{
    pAvFrame = NULL/**pFrameRGB = NULL*/;
    pFormatCtx  = NULL;
    pCodecCtx   = NULL;
    pCodec      = NULL;

    pCvMat = new cv::Mat();
    i=0;
    videoindex=0;

    ret = 0;
    got_picture = 0;
    img_convert_ctx = NULL;
    y_size = 0;
    packet = NULL;

    if (NULL == file)
    {
        filepath =  "opencv.h264";
    }
    else
    {
        filepath = file;
    }

    init();
    openDecode();
    prepare();

    return;
}

void ffmpegDecode :: init()
{
    //ffmpeg注冊復(fù)用器,編碼器等的函數(shù)av_register_all()食棕。
    //該函數(shù)在所有基于ffmpeg的應(yīng)用程序中幾乎都是第一個(gè)被調(diào)用的朗和。只有調(diào)用了該函數(shù),才能使用復(fù)用器簿晓,編碼器等眶拉。
    //這里注冊了所有的文件格式和編解碼器的庫,所以它們將被自動(dòng)的使用在被打開的合適格式的文件上憔儿。注意你只需要調(diào)用 av_register_all()一次忆植,因此我們在主函數(shù)main()中來調(diào)用它。如果你喜歡谒臼,也可以只注冊特定的格式和編解碼器朝刊,但是通常你沒有必要這樣做。
    av_register_all();

    //pFormatCtx = avformat_alloc_context();
    //打開視頻文件,通過參數(shù)filepath來獲得文件名蜈缤。這個(gè)函數(shù)讀取文件的頭部并且把信息保存到我們給的AVFormatContext結(jié)構(gòu)體中拾氓。
    //最后2個(gè)參數(shù)用來指定特殊的文件格式,緩沖大小和格式參數(shù)底哥,但如果把它們設(shè)置為空NULL或者0咙鞍,libavformat將自動(dòng)檢測這些參數(shù)。
    if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0)
    {
        printf("無法打開文件\n");
        return;
    }

    //查找文件的流信息,avformat_open_input函數(shù)只是檢測了文件的頭部趾徽,接著要檢查在文件中的流的信息
    if(av_find_stream_info(pFormatCtx)<0)
    {
        printf("Couldn't find stream information.\n");
        return;
    }
    return;
}

void ffmpegDecode :: openDecode()
{
    //遍歷文件的各個(gè)流续滋,找到第一個(gè)視頻流,并記錄該流的編碼信息
    videoindex = -1;
    for(i=0; i<pFormatCtx->nb_streams; i++) 
    {
        if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
        {
            videoindex=i;
            break;
        }
    }
    if(videoindex==-1)
    {
        printf("Didn't find a video stream.\n");
        return;
    }
    pCodecCtx=pFormatCtx->streams[videoindex]->codec;

    //在庫里面查找支持該格式的解碼器
    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
    if(pCodec==NULL)
    {
        printf("Codec not found.\n");
        return;
    }

    //打開解碼器
    if(avcodec_open2(pCodecCtx, pCodec,NULL) < 0)
    {
        printf("Could not open codec.\n");
        return;
    }
}

void ffmpegDecode :: prepare()
{
    //分配一個(gè)幀指針孵奶,指向解碼后的原始幀
    pAvFrame=avcodec_alloc_frame();
    y_size = pCodecCtx->width * pCodecCtx->height;
    //分配幀內(nèi)存
    packet=(AVPacket *)av_malloc(sizeof(AVPacket));
    av_new_packet(packet, y_size);

    //輸出一下信息-----------------------------
    printf("文件信息-----------------------------------------\n");
    av_dump_format(pFormatCtx,0,filepath,0);
    //av_dump_format只是個(gè)調(diào)試函數(shù)吃粒,輸出文件的音、視頻流的基本信息了拒课,幀率、分辨率事示、音頻采樣等等
    printf("-------------------------------------------------\n");
}

int ffmpegDecode :: readOneFrame()
{
    int result = 0;
    result = av_read_frame(pFormatCtx, packet);
    return result;
}

cv::Mat ffmpegDecode :: getDecodedFrame()
{
    if(packet->stream_index==videoindex)
    {
        //解碼一個(gè)幀
        ret = avcodec_decode_video2(pCodecCtx, pAvFrame, &got_picture, packet);
        if(ret < 0)
        {
            printf("解碼錯(cuò)誤\n");
            return cv::Mat();
        }
        if(got_picture)
        {
            //根據(jù)編碼信息設(shè)置渲染格式
            if(img_convert_ctx == NULL){
                img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
                    pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
                    AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL); 
            }   
            //----------------------opencv
            if (pCvMat->empty())
            {
                pCvMat->create(cv::Size(pCodecCtx->width, pCodecCtx->height),CV_8UC3);
            }

            if(img_convert_ctx != NULL)  
            {  
                get(pCodecCtx, img_convert_ctx, pAvFrame);
            }
        }
    }
    av_free_packet(packet);

    return *pCvMat;
}

cv::Mat ffmpegDecode :: getLastFrame()
{
    ret = avcodec_decode_video2(pCodecCtx, pAvFrame, &got_picture, packet);
    if(got_picture) 
    {  
        //根據(jù)編碼信息設(shè)置渲染格式
        img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL); 

        if(img_convert_ctx != NULL)  
        {  
            get(pCodecCtx, img_convert_ctx,pAvFrame);
        }  
    } 
    return *pCvMat;
}

void ffmpegDecode :: get(AVCodecContext * pCodecCtx, SwsContext * img_convert_ctx, AVFrame * pFrame)
{
    if (pCvMat->empty())
    {
        pCvMat->create(cv::Size(pCodecCtx->width, pCodecCtx->height),CV_8UC3);
    }

    AVFrame *pFrameRGB = NULL;
    uint8_t  *out_bufferRGB = NULL;
    pFrameRGB = avcodec_alloc_frame();

    //給pFrameRGB幀加上分配的內(nèi)存;
    int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);
    out_bufferRGB = new uint8_t[size];
    avpicture_fill((AVPicture *)pFrameRGB, out_bufferRGB, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);

    //YUV to RGB
    sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
    
    memcpy(pCvMat->data,out_bufferRGB,size);

    delete[] out_bufferRGB;
    av_free(pFrameRGB);
}

轉(zhuǎn)載請注明作者Jason Ding及其出處
Github主頁(http://jasonding1354.github.io/)
CSDN博客(http://blog.csdn.net/jasonding1354)
簡書主頁(http://www.reibang.com/users/2bd9b48f6ea8/latest_articles)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末早像,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子肖爵,更是在濱河造成了極大的恐慌卢鹦,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異冀自,居然都是意外死亡揉稚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門熬粗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搀玖,“玉大人,你說我怎么就攤上這事驻呐」嘧纾” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵含末,是天一觀的道長猜拾。 經(jīng)常有香客問我,道長佣盒,這世上最難降的妖魔是什么挎袜? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮肥惭,結(jié)果婚禮上盯仪,老公的妹妹穿的比我還像新娘。我一直安慰自己务豺,他們只是感情好磨总,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著笼沥,像睡著了一般蚪燕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奔浅,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天馆纳,我揣著相機(jī)與錄音,去河邊找鬼汹桦。 笑死鲁驶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的舞骆。 我是一名探鬼主播钥弯,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼督禽!你這毒婦竟也來了脆霎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤狈惫,失蹤者是張志新(化名)和其女友劉穎睛蛛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忆肾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年荸频,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片客冈。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旭从,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出郊酒,到底是詐尸還是另有隱情遇绞,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布燎窘,位于F島的核電站摹闽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏褐健。R本人自食惡果不足惜付鹿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蚜迅。 院中可真熱鬧舵匾,春花似錦、人聲如沸谁不。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽刹帕。三九已至吵血,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間偷溺,已是汗流浹背蹋辅。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挫掏,地道東北人峦阁。 一個(gè)月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓蓖宦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親桐臊。 傳聞我的和親對象是個(gè)殘疾皇子屹培,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

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

  • 原文地址:http://blog.csdn.net/yipie/article/details/7912291 摘...
    冬的天閱讀 7,176評論 1 6
  • 教程一:視頻截圖(Tutorial 01: Making Screencaps) 首先我們需要了解視頻文件的一些基...
    90后的思維閱讀 4,695評論 0 3
  • 夜深了遏佣,故事客棧內(nèi)的人們卻還未歇息咏花。 大廳里的每一盞燈都是亮著矗蕊,周六的夜晚,點(diǎn)滴的夜色都被人們貪婪的索求杠河,彷彿只要...
    旅人的故事客棧閱讀 283評論 0 1
  • 每年都有一撥考研人券敌,轟轟烈烈而來又匆匆走去唾戚。有的人想不清初衷就急急忙忙跟隨考研大軍參加到戰(zhàn)斗中去。 點(diǎn)開知乎待诅,搜到...
    清野記閱讀 513評論 0 3
  • 從小區(qū)北門出發(fā) 一路頂風(fēng)向南 共計(jì)1075步 冬季小鎮(zhèn)的早晨 有人一路狂奔 有人清閑踱步 左手邊的學(xué)生做早操 右手...
    小f呀閱讀 279評論 0 0