FFmpeg-3.3.1移植到Android平臺(Mac編譯)

閑話不多說胯杭,我們的目標(biāo)是:

1.mac平臺編譯FFmpeg生成so動態(tài)庫上遥。
2.Android通過cmake集成,使用so動態(tài)庫。

環(huán)境

mac os: 10.12.6
ndk: 15.1.4119039
android studio: 2.3.3
cmake: cmake_minimum_required(VERSION 3.4.1)

前置條件:

1:NDK環(huán)境配置

#編輯本地配置文件,沒有自行創(chuàng)建
vim .bash_profile

順便把a(bǔ)ndroid環(huán)境也配置好蔗怠,后面會用到

#重新加載配置文件
source .bash_profile
#驗證是否配置正確
ndk-build
image.png

出現(xiàn)上述結(jié)果說明已經(jīng)能找到ndk

2.下載FFmpeg-3.3.1源碼

image.png

這個地方點擊Sourcecode世剖,要吐槽一下他們的設(shè)計逃糟,開始以為要點上面的那個按鈕捷绑,點了半天才反應(yīng)過來要點下面的字。

image.png

解壓之后放到一個目錄:
這里目錄名稱可以隨便劫谅,我統(tǒng)一都放在ffmpeg下
/Users/leon/Desktop/ffmpeg


image.png

2:Mac編譯腳本編寫(坑點頗多)

在目錄下新建ffmpeg_build.sh(名稱可修改)

image.png

此處我用Sublime Text編輯见坑,最好不要用其他平臺的文件,比如從window平臺復(fù)制一份已經(jīng)寫好的腳本文件,可能出現(xiàn)字符編碼問題.所以我為了避免麻煩直接在Mac平臺重寫.

#!/bin/bash
# 因為我是在根目錄捏检,所以要先進(jìn)入ffmpeg-3.3.1目錄
cd ffmpeg-3.3.1
make clean
# NDK的路徑荞驴,根據(jù)自己的安裝位置進(jìn)行設(shè)置
export NDK=/Users/leon/develop/android/android-sdk-macosx/ndk-bundle
export SYSROOT=$NDK/platforms/android-16/arch-arm/
export TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
export CPU=arm
export PREFIX=$(pwd)/android/$CPU
export ADDI_CFLAGS="-marm"
function build_one
{
./configure \
    --prefix=$PREFIX \
    --target-os=linux \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --arch=arm \
    --sysroot=$SYSROOT \
    --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
    --extra-ldflags="$ADDI_LDFLAGS" \
    --cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc \
    --nm=$TOOLCHAIN/bin/arm-linux-androideabi-nm \
    --enable-shared \
    --enable-runtime-cpudetect \
    --enable-gpl \
    --enable-small \
    --enable-cross-compile \
    --disable-debug \
    --disable-static \
    --disable-doc \
    --disable-asm \
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-ffserver \
    --enable-postproc \
    --enable-avdevice \
    --disable-symver \
    --disable-stripping \
$ADDITIONAL_CONFIGURE_FLAG
sed -i '' 's/HAVE_LRINT 0/HAVE_LRINT 1/g' config.h
sed -i '' 's/HAVE_LRINTF 0/HAVE_LRINTF 1/g' config.h
sed -i '' 's/HAVE_ROUND 0/HAVE_ROUND 1/g' config.h
sed -i '' 's/HAVE_ROUNDF 0/HAVE_ROUNDF 1/g' config.h
sed -i '' 's/HAVE_TRUNC 0/HAVE_TRUNC 1/g' config.h
sed -i '' 's/HAVE_TRUNCF 0/HAVE_TRUNCF 1/g' config.h
sed -i '' 's/HAVE_CBRT 0/HAVE_CBRT 1/g' config.h
sed -i '' 's/HAVE_RINT 0/HAVE_RINT 1/g' config.h
make clean
# 這里是定義用幾個CPU編譯,我用4個贯城,一般在5分鐘之內(nèi)編譯完成
make -j4
make install
}
build_one

這個文件在編譯時會出現(xiàn)各種奇怪問題熊楼,其中一個坑是在每行語句后面不能有空格.比如

image.png

3.修改configure文件(修改前最好backup一份)

image.png

修改如下所示:
大概在3300行,注釋前四行掉能犯,然后換成沒有注釋的

#SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
#LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
#SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
#SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'

SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
image.png

這里要修改的目的是因為如果不修改生成so文件的名稱大概是 xxxx.so.5.60 ,版本號跟在了.so后面鲫骗,拿到android下無法正確讀取犬耻,即使手動改變文件名稱也不行.
修改后版本號是在.so前面,文件是以.so結(jié)尾.

*這里進(jìn)入此目錄挎峦,執(zhí)行configure文件生成一些xx.mk配置文件,主要目的是讀取本地編譯環(huán)境信息并記錄香追。

./configure --disable-yasm

4:編譯

激動人心的時刻要到了合瓢,大部分時候都是要折騰很久才能爽這一下坦胶,不好意思,又要開車了晴楔。

# 命令行到腳本所在目錄
./ffmpeg_build.sh

此時正確的姿勢是這樣的

雖然其中會出現(xiàn)這種情況顿苇,但是不必管他

image.png

第一次折騰好時,一看表已經(jīng)凌晨2點,說多了都是淚.

image.png

編譯結(jié)束,在FFmpeg-3.3.1目錄中會多一個android目錄,這里面就是我們要用到的so

image.png

在開頭我們定的目標(biāo)一已經(jīng)完成税弃,來看一下我們目標(biāo)二.

下面開始使用Android Studio創(chuàng)建App工程纪岁,并使用以上我們編譯生成的動態(tài)庫,編寫一個簡單的jni調(diào)用ffmpeg播放本地視頻文件

5:創(chuàng)建Android項目

image.png
image.png

可以先運行一下項目则果,保證新創(chuàng)建的項目沒有問題,這里模擬器不能使用x86架構(gòu)的幔翰,但是arm架構(gòu)的模擬器又巨慢,我用真機(jī)測試

  • main文件目錄中新建 jniLibs文件件
  • 把本地編譯的include文件夾和so賦值過來
image.png

6:編寫CMakeLists

cmake_minimum_required(VERSION 3.4.1)
add_library( native-ffmpeg
             SHARED
             src/main/cpp/native-ffmpeg.cpp )
find_library( log-lib
              log )
find_library( android-lib
              android )
set(distribution_DIR ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
add_library( avutil-55
             SHARED
             IMPORTED )
set_target_properties( avutil-55
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/libavutil-55.so)
add_library( swresample-2
             SHARED
             IMPORTED )
set_target_properties( swresample-2
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/libswresample-2.so)
add_library( avcodec-57
             SHARED
             IMPORTED )
set_target_properties( avcodec-57
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/libavcodec-57.so)
add_library( avfilter-6
             SHARED
             IMPORTED )
set_target_properties( avfilter-6
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/libavfilter-6.so)
add_library( swscale-4
             SHARED
             IMPORTED )
set_target_properties( swscale-4
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/libswscale-4.so)
add_library( avdevice-57
             SHARED
             IMPORTED )
set_target_properties( avdevice-57
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/libavdevice-57.so)
add_library( avformat-57
             SHARED
             IMPORTED )
set_target_properties( avformat-57
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/libavformat-57.so)
add_library( postproc-54
             SHARED
             IMPORTED )
set_target_properties( postproc-54
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/libpostproc-54.so)
set(CMAKE_VERBOSE_MAKEFILE on)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
include_directories(src/main/cpp)
include_directories(src/main/jniLibs/include)

target_link_libraries(native-ffmpeg
                      avutil-55       #工具庫(大部分需要)
                      swresample-2    #音頻采樣數(shù)據(jù)格式轉(zhuǎn)換
                      avcodec-57      #編解碼(重要)
                      avfilter-6      #濾鏡特效處理
                      swscale-4       #視頻像素數(shù)據(jù)格式轉(zhuǎn)換
                      avdevice-57     #各種設(shè)備的輸入輸出
                      avformat-57     #封裝格式處理
                      postproc-54     #后加工
                      ${log-lib}
                      ${android-lib})

這里cmake需要看下官方文檔了解下

7:編寫JNI文件

image.png

1)java控制類:PlayerNative.java

package com.example.leon.ffmpegandroiddemo;

/**
 * Created by leon
 */

public class PlayerNative {

    /**
     * 這里加載有依賴關(guān)系
     */
    static {
        System.loadLibrary("avutil-55");
        System.loadLibrary("swresample-2");
        System.loadLibrary("avcodec-57");
        System.loadLibrary("avfilter-6");
        System.loadLibrary("swscale-4");
        System.loadLibrary("avdevice-57");
        System.loadLibrary("avformat-57");
        System.loadLibrary("postproc-54");
        System.loadLibrary("native-ffmpeg");
    }


    /**
     * 音視頻解碼播放
     *
     * @param path
     * @param view
     */
    public native static void paly(String path, Object view);

    /**
     * 音視頻解碼停止
     *
     */
    public native static void stop();

}

2)Jni:native-ffmpeg.cpp(可以不用寫.h文件)

#include <jni.h>
#include <string>
#include "native-ffmpeg.h"
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include <unistd.h>
#include <android/log.h>

#define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO,"TAG",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"TAG",FORMAT,##__VA_ARGS__);

extern "C" {
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include <libavfilter/avfiltergraph.h>
#include "libavfilter/avfilter.h"
#include "libavutil/imgutils.h"
#include "libavutil/avutil.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libavcodec/avcodec.h"
}

extern "C"
/**
 * 播放
 */
JNIEXPORT void JNICALL Java_com_example_leon_ffmpegandroiddemo_PlayerNative_paly
        (JNIEnv *env, jclass cls, jstring path_, jobject view) {
    LOGE("%s", "play()");
    const char *path = env->GetStringUTFChars(path_, 0);
    //注冊所有的編解碼器
    av_register_all();
    int ret;
    //封裝格式上線文
    AVFormatContext *fmt_ctx = avformat_alloc_context();
    //打開輸入流并讀取頭文件。此時編解碼器還沒有打開
    if (avformat_open_input(&fmt_ctx, path, NULL, NULL) < 0) {
        return;
    }
    //獲取信息
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
        return;
    }
    //獲取視頻流的索引位置
    int video_stream_index = -1;
    for (int i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index = i;
            LOGE("找到視頻流索引位置video_stream_index=%d", video_stream_index);
            break;
        }
    }
    if (video_stream_index == -1) {
        LOGE("未找到視頻流索引");
    }
    ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, view);
    if (nativeWindow == NULL) {
        LOGE("ANativeWindow_fromSurface error");
        return;
    }
    //繪制時候的緩沖區(qū)
    ANativeWindow_Buffer outBuffer;
    //獲取視頻流解碼器

    AVCodecContext *codec_ctx = avcodec_alloc_context3(NULL);
    avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[video_stream_index]->codecpar);
    AVCodec *avCodec = avcodec_find_decoder(codec_ctx->codec_id);
    //打開解碼器
    if ((ret = avcodec_open2(codec_ctx, avCodec, NULL)) < 0) {
        ret = -3;
        return;
    }
    //循環(huán)從文件讀取一幀壓縮數(shù)據(jù)
    //開始讀取視頻
    int y_size = codec_ctx->width * codec_ctx->height;
    AVPacket *pkt = (AVPacket *) malloc(sizeof(AVPacket));//分配一個packet
    av_new_packet(pkt, y_size);//分配packet的數(shù)據(jù)
    AVFrame *yuvFrame = av_frame_alloc();
    AVFrame *rgbFrame = av_frame_alloc();
    // 顏色轉(zhuǎn)換器
    SwsContext *m_swsCtx = sws_getContext(codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt,
                                          codec_ctx->width,
                                          codec_ctx->height, AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL,
                                          NULL, NULL);


    LOGE("開始解碼");
    int index = 0;
    while (1) {
        if (av_read_frame(fmt_ctx, pkt) < 0) {
            //這里就認(rèn)為視頻讀完了
            break;
        }
        if (pkt->stream_index == video_stream_index) {
            //視頻解碼
            ret = avcodec_send_packet(codec_ctx, pkt);
            if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
                LOGE("avcodec_send_packet ret=%d", ret);
                av_packet_unref(pkt);
                continue;
            }
            //從解碼器返回解碼輸出數(shù)據(jù)
            ret = avcodec_receive_frame(codec_ctx, yuvFrame);
            if (ret < 0 && ret != AVERROR_EOF) {
                LOGE("avcodec_receive_frame ret=%d", ret);
                av_packet_unref(pkt);
                continue;
            }
            //avcodec_decode_video2(codec_ctx,yuvFrame,&got_pictue,&pkt);
            sws_scale(m_swsCtx, (const uint8_t *const *) yuvFrame->data, yuvFrame->linesize, 0,
                      codec_ctx->height, rgbFrame->data, rgbFrame->linesize);
            //設(shè)置緩沖區(qū)的屬性
            ANativeWindow_setBuffersGeometry(nativeWindow, codec_ctx->width, codec_ctx->height,
                                             WINDOW_FORMAT_RGBA_8888);
            ret = ANativeWindow_lock(nativeWindow, &outBuffer, NULL);
            if (ret != 0) {
                LOGE("ANativeWindow_lock error");
                return;
            }
            av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize,
                                 (const uint8_t *) outBuffer.bits, AV_PIX_FMT_RGBA,
                                 codec_ctx->width, codec_ctx->height, 1);
            //fill_ANativeWindow(&outBuffer,outBuffer.bits,rgbFrame);
            //將緩沖區(qū)數(shù)據(jù)顯示到surfaceView
            ret = ANativeWindow_unlockAndPost(nativeWindow);
            if (ret != 0) {
                LOGE("ANativeWindow_unlockAndPost error");
                return;
            }
            LOGE("成功顯示到緩沖區(qū)%d次", ++index);
        }
        av_packet_unref(pkt);
        usleep(1000 * 8);

    }

    av_frame_free(&rgbFrame);
    avcodec_close(codec_ctx);
    sws_freeContext(m_swsCtx);
    avformat_close_input(&fmt_ctx);
    ANativeWindow_release(nativeWindow);
    env->ReleaseStringUTFChars(path_, path);
    LOGI("解析完成");
}

/**
 * 暫停
 */
JNIEXPORT void JNICALL Java_com_example_leon_ffmpegandroiddemo_PlayerNative_stop
        (JNIEnv *env, jclass cls) {
    LOGE("%s", "stop()");
}

3)其他文件
MainActivity.java

package com.example.leon.ffmpegandroiddemo;

import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import java.io.File;

public class MainActivity extends AppCompatActivity {


    private VideoView mVideo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mVideo = (VideoView) findViewById(R.id.video);
    }


    public void paly(View view) {
        final String dir = Environment.getExternalStorageDirectory() + File.separator + "Download";
        final String path = dir + File.separator + "1.mp4";
        final VideoView mVideo = (VideoView) findViewById(R.id.video);
        if (new File(path).exists()) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    PlayerNative.paly(path, mVideo.getHolder().getSurface());
                }
            });
        } else {
            System.out.println("文件不存在");
        }
    }

    public void stop(View view) {
        PlayerNative.stop();
    }
}

VideoView.java

package com.example.leon.ffmpegandroiddemo;

import android.content.Context;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 *
 * Created by leon
 */
public class VideoView extends SurfaceView {
    public VideoView(Context context) {
        super(context);
        init();
    }

    public VideoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public VideoView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        SurfaceHolder holder = getHolder();
        holder.setFormat(PixelFormat.RGBA_8888);
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="paly"
            android:text="開始" />

        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="stop"
            android:text="結(jié)束" />
    </LinearLayout>


    <com.example.leon.ffmpegandroiddemo.VideoView
        android:id="@+id/video"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="10dp" />

</LinearLayout>

到此我們定的兩個目標(biāo)已經(jīng)完成西壮,以后會再繼續(xù)寫FFmpeg使用教程遗增。

最后總結(jié)一下各種問題:
1:cannot locate symbol "atof" referenced by "libavformat-57.so"...的問題

原因:Android的stdlib.h中atof是內(nèi)聯(lián)的
解決方法:將所有的atof改成strtod
示例代碼:
char *strpi = "3.1415";
double dpi;
dpi = atof(strpi); 修改為: dpi = strtod(strpi, NULL);

或者腳本文件中定義platforms版本使用低版本,比如
export SYSROOT=$NDK/platforms/android-16/arch-arm/

2.cannot locate symbol "log2f" referenced by "libavcodec-57.so"..
原因: 這個跟ndk與android版本有關(guān)。如果使用高版本ndk編譯,so會判斷系統(tǒng)版本并使用系統(tǒng)log款青,但實際手機(jī)系統(tǒng)版本較低做修,并沒有此方法,所以會報找不到此方法
解決辦法:
1),使用platforms 低版本
2),修改 ./libavutil/libm.h里面的定義,不再判斷是否已經(jīng)存在函數(shù)抡草。使用重新定義

//#if !HAVE_LOG2  
//#undef log2  
#define log2(x) (log(x) * 1.44269504088896340736)  
//#endif /* HAVE_LOG2 */  
  
//#if !HAVE_LOG2F  
//#undef log2f  
#define log2f(x) ((float)log2(x))  
//#endif /* HAVE_LOG2F */  
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末饰及,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子康震,更是在濱河造成了極大的恐慌燎含,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腿短,死亡現(xiàn)場離奇詭異屏箍,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)答姥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門铣除,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鹦付,你說我怎么就攤上這事尚粘。” “怎么了敲长?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵郎嫁,是天一觀的道長秉继。 經(jīng)常有香客問我,道長泽铛,這世上最難降的妖魔是什么尚辑? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮盔腔,結(jié)果婚禮上杠茬,老公的妹妹穿的比我還像新娘。我一直安慰自己弛随,他們只是感情好瓢喉,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著舀透,像睡著了一般栓票。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上愕够,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天走贪,我揣著相機(jī)與錄音,去河邊找鬼惑芭。 笑死坠狡,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的强衡。 我是一名探鬼主播擦秽,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼漩勤!你這毒婦竟也來了感挥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤越败,失蹤者是張志新(化名)和其女友劉穎触幼,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體究飞,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡置谦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了亿傅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媒峡。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖葵擎,靈堂內(nèi)的尸體忽然破棺而出谅阿,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布签餐,位于F島的核電站寓涨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏氯檐。R本人自食惡果不足惜戒良,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望冠摄。 院中可真熱鬧糯崎,春花似錦、人聲如沸耗拓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乔询。三九已至,卻和暖如春韵洋,著一層夾襖步出監(jiān)牢的瞬間竿刁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工搪缨, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留食拜,地道東北人。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓副编,卻偏偏與公主長得像负甸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子痹届,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

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