FFmpeg muxing示例程序解析

FFmpeg可能是當今視/音頻領(lǐng)域應(yīng)用最為廣泛的開源項目了愉舔,國內(nèi)許多著名的影音程序或多或少地都用了它的代碼。作為視/音頻領(lǐng)域研究或開發(fā)的人光羞,無論如何都不應(yīng)該錯過這個項目逆航。本文就拿FFmpeg示例程序中的muxing.c文件美旧,來對FFmpeg的使用作一篇簡要介紹渤滞。胖兔的老習(xí)慣,仍然是直接從代碼開擼榴嗅。先看文件中定義的OutputSteam數(shù)據(jù)結(jié)構(gòu):

typedef struct OutputStream {
    AVStream *st;  //視頻或音頻流
    AVCodecContext *enc; //編碼配置

    int64_t next_pts; //下一幀的PTS妄呕,用于視/音頻同步
    int samples_count; //聲音采樣計數(shù)

    AVFrame *frame;  //視頻/音頻幀
    AVFrame *tmp_frame; //臨時幀

    float t, tincr, tincr2; //用于聲音生成

    struct SwsContext *sws_ctx; //視頻轉(zhuǎn)換配置
    struct SwrContext *swr_ctx; //聲音重采樣配置
} OutputStream;

對視/音頻文件的操作,實際上都是針對視頻/音頻流來進行的嗽测。這個OutputStream類就是用于操作視頻/音頻流的包裝類绪励。

接下來從主函數(shù)開始,按順序梳理整個代碼流程:

if (argc < 2) {
  printf("usage: %s output_file\n"
    "API example program to output a media file with libavformat.\n"
    "This program generates a synthetic audio and video stream, encodes and\n"
    "muxes them into a file named output_file.\n"
    "The output format is automatically guessed according to the file extension.\n"
    "Raw images can also be output by using '%%d' in the filename.\n"
    "\n", argv[0]);
  return 1;
}

這里介紹了示例程序的功能和使用方法唠粥。運行本程序的時候要帶一個輸出文件名參數(shù)疏魏,然后程序?qū)⑸梢粋€同步的視頻和音頻流,編碼復(fù)用到指定的文件中去晤愧。輸出的格式是根據(jù)給定的文件擴展名自動猜取的大莫。

filename = argv[1];
for (i = 2; i+1 < argc; i+=2) {
  if (!strcmp(argv[i], "-flags") || !strcmp(argv[i], "-fflags"))
  av_dict_set(&opt, argv[i]+1, argv[i+1], 0);
}

這里檢查程序啟動有沒有帶其他參數(shù),有的話納入到參數(shù)字典养涮。對于不甚精通音/視頻技術(shù)的初學(xué)者來說葵硕,這里直接忽略就好了眉抬。

avformat_alloc_output_context2(&oc, NULL, NULL, filename);
if (!oc) {
  printf("Could not deduce output format from file extension: using MPEG.\n");
  avformat_alloc_output_context2(&oc, NULL, "mpeg", filename);
}
fmt = oc->oformat;

這里初始化了AVFormatContext(格式配置)贯吓,它在FFmpeg程序里是貫穿始終的一個類,非常重要蜀变。注意avformat_alloc_output_context2這個函數(shù)悄谐,它的第二個參數(shù)可以是一個AVFormat實例,用來決定視頻/音頻格式库北,如果被設(shè)為NULL就繼續(xù)看第三個參數(shù)爬舰,這是一個描述格式的字符串们陆,比如可以是“h264"、 "mpeg"等情屹;如果它也是NULL坪仇,就看最后第四個filename,從它的擴展名來推斷應(yīng)該使用的格式垃你。比如用戶指定的文件名是”test.avi"椅文,就會使用普通的AVI格式。有人說那我用h264格式但文件名就想用.avi行不行惜颇,當然可以皆刺,把第三個參數(shù)設(shè)為"h264"就行了,這時就不會從文件名來推斷格式了凌摄。

if (fmt->video_codec != AV_CODEC_ID_NONE) {
  add_stream(&video_st, oc, &video_codec, fmt->video_codec);
  have_video = 1;
  encode_video = 1;
}
if (fmt->audio_codec != AV_CODEC_ID_NONE) {
  add_stream(&audio_st, oc, &audio_codec, fmt->audio_codec);
  have_audio = 1;
  encode_audio = 1;
}

接下來根據(jù)推斷出的格式添加視頻/音頻流羡蛾。如果給定的是"mp4"這樣的格式,默認是既有視頻也有音頻锨亏;如果給定的是"mp3"痴怨,那就只有音頻沒有視頻了。我們暫停一下main函數(shù)屯伞,去看看add_stream函數(shù)是如何定義的腿箩,注意筆者添加的中文注釋(代碼有刪節(jié),便于突出主要流程劣摇。本文后續(xù)其他代碼同樣處理):

void add_stream(OutputStream *ost, AVFormatContext *oc,
  AVCodec **codec, enum AVCodecID codec_id)
{
  //根據(jù)推斷出的格式珠移,尋找相應(yīng)的AVCodec編碼
  *codec = avcodec_find_encoder(codec_id);
  //分配一個視頻/音頻流,這里的ost就是本文一開頭分析的OutputStream結(jié)構(gòu)數(shù)據(jù)
  ost->st = avformat_new_stream(oc, NULL);
  //設(shè)定流ID號末融,與流在文件中的序號對應(yīng)(一個文件中可以有多個視頻/音頻流)
  ost->st->id = oc->nb_streams-1;
  //分配CodecContext編碼上下文钧惧,存入OutputStream結(jié)構(gòu)
  AVCodecContext *c = avcodec_alloc_context3(*codec);
  ost->enc = c;
  //根據(jù)視頻、音頻不同類型勾习,初始化CodecContext編碼配置
  switch ((*codec)->type) {
  case AVMEDIA_TYPE_AUDIO: //這部分是音頻數(shù)據(jù)
    c->sample_fmt = (*codec)->sample_fmts ? (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP; //采樣格式
    c->bit_rate = 64000;  //碼率
    c->sample_rate = 44100;  //采樣速率
    c->channels = av_get_channel_layout_nb_channels(c->channel_layout); //聲道數(shù)
    c->channel_layout = AV_CH_LAYOUT_STEREO; //聲道布局
    c->channels = av_get_channel_layout_nb_channels(c->channel_layout);
    ost->st->time_base = (AVRational){ 1, c->sample_rate }; //計時基準
    break;
  case AVMEDIA_TYPE_VIDEO: //這部分是視頻數(shù)據(jù)
    c->codec_id = codec_id; //視頻編碼
    c->bit_rate = 400000; //碼率
    c->width = 352; //視頻寬高浓瞪,注意必須是雙數(shù),YUV420P格式要求
    c->height = 288;
    ost->st->time_base = (AVRational){ 1, STREAM_FRAME_RATE }; //計時基準
    c->time_base = ost->st->time_base;
    c->gop_size = 12;
    c->pix_fmt = STREAM_PIX_FMT;
    break;
  }
  //是否需要分離的Stream Header
  if (oc->oformat->flags & AVFMT_GLOBALHEADER)
    c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}

敲黑板巧婶!這里有重點乾颁!我們知道做視/音頻程序,必須要考慮視頻和音頻數(shù)據(jù)的同步問題艺栈,技術(shù)上怎么實現(xiàn)英岭?請看上面代碼中的time_base,用來設(shè)定計時基準湿右,它來源于圖像/聲音采集原理诅妹。對于視頻,我們知道人眼視覺殘留的時間是1/24秒,視頻只要達到每秒24幀以上人就不會覺得有閃爍或卡頓吭狡,一般會設(shè)成25尖殃,也就是代碼中的STREAM_FRAME_RATE常數(shù),視頻time_base設(shè)為1/25划煮,也就是每一個視頻幀停留1/25秒送丰。再看音頻,聲音的采樣是指一秒內(nèi)采集多少次聲音數(shù)據(jù)弛秋,采樣頻率越高聲音質(zhì)量越好蚪战,44.1kHz就可以達到CD音響質(zhì)量,也是MPEG標準聲音質(zhì)量铐懊。那么它的基準就是1/44100邀桑。

繼續(xù)接著看main函數(shù):

if (have_video)
  open_video(oc, video_codec, &video_st, opt);
if (have_audio)
  open_audio(oc, audio_codec, &audio_st, opt);

所有參數(shù)都設(shè)好了,可以打開視頻/音頻編碼科乎,分配必要的緩沖區(qū)了壁畸。先看open_video函數(shù):

void open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg)
{
  AVCodecContext *c = ost->enc;
  AVDictionary *opt = NULL;
  //拷貝用戶設(shè)定的參數(shù)字典
  av_dict_copy(&opt, opt_arg, 0);
  //打開編碼器,隨后釋放參數(shù)字典
  avcodec_open2(c, codec, &opt);
  av_dict_free(&opt);
  //分配并初始化一個可重復(fù)使用的視頻幀茅茂,指定好像素點格式和寬高
  ost->frame = alloc_picture(c->pix_fmt, c->width, c->height);
  //如果輸出格式不是YUV420P捏萍,那么需要一個臨時的YUV420P幀便于進行轉(zhuǎn)換
  ost->tmp_frame = NULL;
  if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
    ost->tmp_frame = alloc_picture(AV_PIX_FMT_YUV420P, c->width, c->height);
  }
  //從CodecContext中拷貝參數(shù)到流/復(fù)用器
  avcodec_parameters_from_context(ost->st->codecpar, c);
}

上面的代碼使用了alloc_picture函數(shù)來分配視頻幀。這個函數(shù)是這樣定義的:

AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height)
{
  AVFrame *picture = av_frame_alloc();
  picture->format = pix_fmt;
  picture->width = width;
  picture->height = height;
  //分配幀數(shù)據(jù)緩沖區(qū)
  av_frame_get_buffer(picture, 32);
  return picture;
}

注意av_frame_get_buffer函數(shù)空闲,它為幀數(shù)據(jù)分配緩沖區(qū)令杈,第二個參數(shù)32用于對齊,如果搞不清楚怎么設(shè)的話碴倾,直接設(shè)為0就行逗噩,F(xiàn)Fmpeg會自動處理。

視頻打開了跌榔。接著看打開音頻的open_audio函數(shù):

void open_audio(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg)
{
  int nb_samples;
  AVDictionary *opt = NULL;
  AVCodecContext *c = ost->enc;
  //拷貝參數(shù)字典
  av_dict_copy(&opt, opt_arg, 0);
  //打開編碼器异雁,釋放參數(shù)字典
  avcodec_open2(c, codec, &opt);
  av_dict_free(&opt);
  //初始化信號生成器,用于聲音自動生成
  ost->t = 0;
  ost->tincr = 2 * M_PI * 110.0 / c->sample_rate;
  ost->tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate;
  //采樣大小僧须。如果幀大小固定纲刀,則為frame_size
  if (c->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)
    nb_samples = 10000;
  else
    nb_samples = c->frame_size;
  //分配音頻幀和臨時音頻幀
  ost->frame = alloc_audio_frame(c->sample_fmt, c->channel_layout, c->sample_rate, nb_samples);
  ost->tmp_frame = alloc_audio_frame(AV_SAMPLE_FMT_S16, c->channel_layout, c->sample_rate, nb_samples);
  //從CodecContext中拷貝參數(shù)到流/復(fù)用器
  avcodec_parameters_from_context(ost->st->codecpar, c);
  //創(chuàng)建重采樣配置,設(shè)定聲道數(shù)担平、輸入輸出采樣率示绊、采樣格式等選項
  ost->swr_ctx = swr_alloc();
  av_opt_set_int(ost->swr_ctx, "in_channel_count", c->channels, 0);
  av_opt_set_int(ost->swr_ctx, "in_sample_rate", c->sample_rate, 0);
  av_opt_set_sample_fmt(ost->swr_ctx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
  av_opt_set_int(ost->swr_ctx, "out_channel_count", c->channels, 0);
  av_opt_set_int(ost->swr_ctx, "out_sample_rate", c->sample_rate, 0);
  av_opt_set_sample_fmt(ost->swr_ctx, "out_sample_fmt", c->sample_fmt, 0);
  swr_init(ost->swr_ctx);
}

分配音頻幀使用了alloc_audio_frame函數(shù):

AVFrame *alloc_audio_frame(enum AVSampleFormat sample_fmt,
  uint64_t channel_layout,  int sample_rate, int nb_samples)
{
  AVFrame *frame = av_frame_alloc();
  frame->format = sample_fmt; //采樣格式
  frame->channel_layout = channel_layout; //聲道布局
  frame->sample_rate = sample_rate; //采樣率
  frame->nb_samples = nb_samples; //采樣大小
  if (nb_samples) {
    av_frame_get_buffer(frame, 0);
  }
  return frame;
}

現(xiàn)在視頻/音頻都設(shè)定好了,回到main函數(shù)暂论,接下來看看格式設(shè)定是否正確:

av_dump_format(oc, 0, filename, 1);

這一行在命令行下導(dǎo)出當前格式設(shè)定面褐,執(zhí)行以后輸出示例是這樣的:


Dump Format輸出示例

可以看到,我們設(shè)定的輸出文件名是test2.mp4空另,文件中包含兩個流盆耽,一個視頻流蹋砚,H264格式扼菠,幀格式Y(jié)UV420P摄杂,幀大小352*288;另一個音頻流循榆,AAC格式析恢,采樣率44.1kHz,立體聲秧饮。

繼續(xù)往下看:

//打開輸出文件
avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);
//輸出流的頭部
avformat_write_header(oc, &opt);

OK映挂,現(xiàn)在萬事俱備,只欠寫入了盗尸。接著看視頻和音頻數(shù)據(jù)是如何寫入的:

while (encode_video || encode_audio) {
  if (encode_video && (!encode_audio ||
    av_compare_ts(video_st.next_pts, video_st.enc->time_base,
                  audio_st.next_pts, audio_st.enc->time_base) <= 0)) {
    encode_video = !write_video_frame(oc, &video_st);
  } else {
    encode_audio = !write_audio_frame(oc, &audio_st);
  }
}

這里值得注意的還是視/音頻同步問題柑船。寫入文件的時候,什么時候?qū)懸曨l幀泼各,什么時候?qū)懸纛l幀鞍时?代碼給出了一個辦法,在有視頻無音頻扣蜻,或者視頻時間戳落后于音頻的時候就寫視頻幀逆巍,否則就寫入音頻幀。av_compare_ts函數(shù)用來進行時間戳(Timestamp)比較莽使。

接著看視頻幀是怎么寫入的:

int write_video_frame(AVFormatContext *oc, OutputStream *ost)
{
  AVCodecContext *c = ost->enc;
  //生成視頻幀
  AVFrame *frame = get_video_frame(ost);
  int got_packet = 0;
  AVPacket pkt = { 0 };
  //初始化數(shù)據(jù)包
  av_init_packet(&pkt);
  //將視頻幀編碼壓入數(shù)據(jù)包
  avcodec_encode_video2(c, &pkt, frame, &got_packet);
  if (got_packet) {
    //如果有數(shù)據(jù)包生成锐极,則寫入流
    ret = write_frame(oc, &c->time_base, ost->st, &pkt);
  } else {
    ret = 0;
  }
  return (frame || got_packet) ? 0 : 1;
}

流程比較一目了然。先看get_video_frame函數(shù)是如何生成視頻幀的:

AVFrame *get_video_frame(OutputStream *ost)
{
  AVCodecContext *c = ost->enc;
  //檢查是否繼續(xù)生成視頻幀芳肌。如果超過預(yù)定時長就停止生成
  if (av_compare_ts(ost->next_pts, c->time_base, 
      STREAM_DURATION, (AVRational){ 1, 1 }) >= 0)
    return NULL;
  //使幀數(shù)據(jù)可寫灵再,此處視頻數(shù)據(jù)是代碼生成的,注意frame指針本身不可以修改
  //因為FFmpeg內(nèi)部會引用這個指針亿笤,一旦改了可能會破壞視頻
  av_frame_make_writable(ost->frame)檬嘀;
  //如果目標格式不是YUV420P,那么必須要進行格式轉(zhuǎn)換
  if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
    if (!ost->sws_ctx) {  //先獲取轉(zhuǎn)換環(huán)境
      ost->sws_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_YUV420P,
            c->width, c->height, c->pix_fmt, SCALE_FLAGS, NULL, NULL, NULL);
    }
    //向臨時幀填充數(shù)據(jù)责嚷,之后轉(zhuǎn)換填入當前幀
    fill_yuv_image(ost->tmp_frame, ost->next_pts, c->width, c->height);
    sws_scale(ost->sws_ctx, (const uint8_t * const *) ost->tmp_frame->data,
         ost->tmp_frame->linesize, 0, c->height, ost->frame->data, ost->frame->linesize);
  } else {
    //目標格式就是YUV420P鸳兽,直接轉(zhuǎn)換就可以
    fill_yuv_image(ost->frame, ost->next_pts, c->width, c->height);
  }
  //新幀生成,PTS遞增
  ost->frame->pts = ost->next_pts++;
  return ost->frame;
}

測試程序的視頻幀是由代碼生成的罕拂,具體在fill_yuv_image函數(shù)里:

void fill_yuv_image(AVFrame *pict, int frame_index, int width, int height)
{
  int x, y, i;
  i = frame_index;
  //生成Y
  for (y = 0; y < height; y++)
    for (x = 0; x < width; x++)
      pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3;
  //生成Cb和Cr
  for (y = 0; y < height / 2; y++) {
    for (x = 0; x < width / 2; x++) {
      pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2;
      pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5;
    }
  }
}

這里用代碼揍异,按照一定規(guī)律填寫YUV420P格式的數(shù)據(jù),注意下面的循環(huán)爆班,可以明白為什么視頻的寬和高必須是2的倍數(shù)了吧衷掷。

回到視頻幀寫入函數(shù)write_video_frame,它接下來調(diào)用了avcodec_encode_video2函數(shù)柿菩,將幀數(shù)據(jù)編碼壓入數(shù)據(jù)包戚嗅。然而,這個函數(shù)在最新版FFmpeg里已經(jīng)被廢棄了,新版本采用了更加靈活的編碼方式懦胞。胖兔采用的是以下修改后的代碼:

ret = avcodec_send_frame(c, frame); //將幀送入編碼配置上下文
while (ret >= 0) {
  ret = avcodec_receive_packet(c, &pkt); //循環(huán)接收數(shù)據(jù)包替久,直到所有包接收完成
  if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
    break;
  write_frame(oc, &c->time_base, ost->st, &pkt);
}

對比一下新老代碼,可以看到老代碼是比較死的躏尉,送一幀進去蚯根,只能接收一個包出來;新代碼則允許送一幀進去胀糜,接收N個包出來颅拦。這樣能夠有效避免因為幀編碼壓縮延遲導(dǎo)致數(shù)據(jù)包滯留的問題。

收到數(shù)據(jù)包之后教藻,要把它寫入視頻流距帅,使用的write_frame函數(shù):

int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
{
  //轉(zhuǎn)換時間戳,由數(shù)據(jù)包向視頻流
  av_packet_rescale_ts(pkt, *time_base, st->time_base);
  pkt->stream_index = st->index;  //指明包屬于哪個流
  return av_interleaved_write_frame(fmt_ctx, pkt);  //將包寫入流
}

OK括堤,到這里視頻寫入就結(jié)束了锥债。音頻的采集與編碼過程與之類似。這里不再詳細解析了痊臭,具體可以參見示例程序代碼哮肚。

回到main函數(shù),完成視頻/音頻寫入以后广匙,最后還需要做的就是收尾工作:

av_write_trailer(oc); //寫尾部
if (have_video)
  close_stream(oc, &video_st); //關(guān)閉視頻流
if (have_audio)
  close_stream(oc, &audio_st); //關(guān)閉音頻流
avio_closep(&oc->pb);  //關(guān)閉輸出文件
avformat_free_context(oc); //釋放格式配置上下文

解析結(jié)束允趟。希望對需要的人有所幫助。

最后生成的視頻效果

最后貼一張程序生成的視頻動圖(壓縮后效果一般鸦致,勉強鎮(zhèn)樓潮剪,湊合看看效果吧)。


碼農(nóng)也精彩分唾!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抗碰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绽乔,更是在濱河造成了極大的恐慌弧蝇,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,946評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件折砸,死亡現(xiàn)場離奇詭異看疗,居然都是意外死亡,警方通過查閱死者的電腦和手機睦授,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評論 3 399
  • 文/潘曉璐 我一進店門两芳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人去枷,你說我怎么就攤上這事怖辆∈歉矗” “怎么了?”我有些...
    開封第一講書人閱讀 169,716評論 0 364
  • 文/不壞的土叔 我叫張陵竖螃,是天一觀的道長淑廊。 經(jīng)常有香客問我,道長斑鼻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,222評論 1 300
  • 正文 為了忘掉前任猎荠,我火速辦了婚禮坚弱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘关摇。我一直安慰自己荒叶,他們只是感情好,可當我...
    茶點故事閱讀 69,223評論 6 398
  • 文/花漫 我一把揭開白布输虱。 她就那樣靜靜地躺著些楣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宪睹。 梳的紋絲不亂的頭發(fā)上愁茁,一...
    開封第一講書人閱讀 52,807評論 1 314
  • 那天,我揣著相機與錄音亭病,去河邊找鬼鹅很。 笑死,一個胖子當著我的面吹牛罪帖,可吹牛的內(nèi)容都是我干的促煮。 我是一名探鬼主播,決...
    沈念sama閱讀 41,235評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼整袁,長吁一口氣:“原來是場噩夢啊……” “哼菠齿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起坐昙,我...
    開封第一講書人閱讀 40,189評論 0 277
  • 序言:老撾萬榮一對情侶失蹤绳匀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后炸客,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體襟士,經(jīng)...
    沈念sama閱讀 46,712評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,775評論 3 343
  • 正文 我和宋清朗相戀三年嚷量,在試婚紗的時候發(fā)現(xiàn)自己被綠了陋桂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,926評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡蝶溶,死狀恐怖嗜历,靈堂內(nèi)的尸體忽然破棺而出宣渗,到底是詐尸還是另有隱情,我是刑警寧澤梨州,帶...
    沈念sama閱讀 36,580評論 5 351
  • 正文 年R本政府宣布痕囱,位于F島的核電站,受9級特大地震影響暴匠,放射性物質(zhì)發(fā)生泄漏鞍恢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,259評論 3 336
  • 文/蒙蒙 一每窖、第九天 我趴在偏房一處隱蔽的房頂上張望帮掉。 院中可真熱鬧,春花似錦窒典、人聲如沸蟆炊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涩搓。三九已至,卻和暖如春劈猪,著一層夾襖步出監(jiān)牢的瞬間昧甘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評論 1 274
  • 我被黑心中介騙來泰國打工战得, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留疾层,地道東北人。 一個月前我還...
    沈念sama閱讀 49,368評論 3 379
  • 正文 我出身青樓贡避,卻偏偏與公主長得像痛黎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子刮吧,可洞房花燭夜當晚...
    茶點故事閱讀 45,930評論 2 361

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

  • 教程一:視頻截圖(Tutorial 01: Making Screencaps) 首先我們需要了解視頻文件的一些基...
    90后的思維閱讀 4,713評論 0 3
  • 1湖饱、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,988評論 3 119
  • 清晨,第一縷陽光撥開輕盈的雪紡窗簾杀捻,悄悄地井厌、準時地闖進臥室,我卻始終不愿睜開眼睛致讥。一天的開始仅仆,卻不是在你寵溺...
    毒女笑世閱讀 215評論 0 1
  • 水戲河堤潺潺笑, 花落枝頭竊竊語垢袱。 欲攬春色寄織女墓拜, 不見天宮往來人。 2018.3.11.晨
    開心點金石閱讀 205評論 0 0
  • 【日精進打卡第32天】 寧波禾隆新材料有限公司 打卡人:李亮 【知~學(xué)習(xí)】 《六項精進》1遍 共66遍 《大學(xué)》1...
    禾隆李亮閱讀 156評論 0 0