【Android 音視頻開發(fā)打怪升級(jí):FFmpeg音視頻編解碼篇】二宏榕、Android 引入FFmpeg

【聲 明】

首先拓诸,這一系列文章均基于自己的理解和實(shí)踐,可能有不對(duì)的地方麻昼,歡迎大家指正奠支。
其次,這是一個(gè)入門系列涌献,涉及的知識(shí)也僅限于夠用胚宦,深入的知識(shí)網(wǎng)上也有許許多多的博文供大家學(xué)習(xí)了首有。
最后燕垃,寫文章過程中,會(huì)借鑒參考其他人分享的文章井联,會(huì)在文章最后列出卜壕,感謝這些作者的分享飒房。

碼字不易糊昙,轉(zhuǎn)載請(qǐng)注明出處!

教程代碼:【Github傳送門

目錄

一百炬、Android音視頻硬解碼篇:

二秦驯、使用OpenGL渲染視頻畫面篇

三词身、Android FFmpeg音視頻解碼篇


本文你可以了解到

本文將介紹如何將上一篇文章編譯出來的 FFmpeg so 庫,引入到 Android 工程中墓塌,并驗(yàn)證 so 是否可以正常使用瘟忱。

一奥额、開啟 Android 原生 C/C++ 支持

在過去,通常使用 makefile 的方式在項(xiàng)目中引入 C/C++ 代碼支持访诱,隨著 Android Studio 的普及垫挨,makefile 的方式已經(jīng)基本被 CMake 替代。

有了 Android 官方的支持触菜,NDK 層代碼的開發(fā)變得更加容易九榔。以前一談到 Android NDK ,許多人就會(huì)大驚失色涡相,感覺是深不可測(cè)的東西哲泊,一方面是 makefile 的編寫很難,一方面是 C/C++ 相比 Java 來說催蝗,比較晦澀切威。

但是不必?fù)?dān)心,一是有了 CMake 丙号,二是對(duì)于 C/C++ 的基本使用其實(shí)和 Java 差不多先朦,本系列涉及到的,也都是對(duì) C/C++ 的基礎(chǔ)使用犬缨,畢竟喳魏,高級(jí)的我也不會(huì)不是嗎?哈哈哈~~

1. 安裝 CMake

首先怀薛,需要下載 CMake 相關(guān)工具刺彩,在 Android Studio 中依次點(diǎn)擊 Tools->SDK Manager->SDK Tools,然后勾選

CMake : CMake 構(gòu)建工具

LLDB : C/C++ 代碼調(diào)試工具

NDK : NDK 環(huán)境

最后依次點(diǎn)擊 OK->OK->Finish 乾戏,開始下載(文件比較大鼓择,可能會(huì)比較慢,請(qǐng)耐心等待)摆出。

下載CMake工具

2. 添加 C/C++ 支持

有兩種方式:

一是,新建一個(gè)新的工程偎漫,并勾選 C/C++ 支持選項(xiàng)爷恳,系統(tǒng)將自動(dòng)創(chuàng)建一個(gè)支持 C/C++ 編碼的工程栈虚。

二是曼验,在已有的項(xiàng)目上,手動(dòng)添加所有的添加項(xiàng)來支持 C/C++ 編碼粘姜,其實(shí)就是自己手動(dòng)添加「第一種方式」Android Studio 為我們自動(dòng)創(chuàng)建的那些東西。

首先活喊,通過新建一個(gè)新工程的方式钾菊,看看 IDE 為我們生成了那些東西偎肃。

1)新建 C/C++ 工程

依次點(diǎn)擊 File -> New -> New Project煞烫,進(jìn)入新建工程頁面,拉到最后累颂,選擇 Native C++ 然后按照默認(rèn)配置滞详,一路 Next -> Next -> Finish 即可。

新建C++工程
2)Android Studio 自動(dòng)生成了什么

生成的工程目錄如下:

工程目錄

重點(diǎn)關(guān)注上圖標(biāo)注的3個(gè)地方:

  • 第一紊馏,最上層的 MainActivity
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Example of a call to a native method
        sample_text.text = stringFromJNI()
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    external fun stringFromJNI(): String

    companion object {

        // Used to load the 'native-lib' library on application startup.
        init {
            System.loadLibrary("native-lib")
        }
    }
}

很簡單料饥,使用過 so 庫的應(yīng)該都看得懂,這里簡單說一下朱监。

代碼的最下面岸啡,companion objectKotlin 中表示靜態(tài)代碼塊,類似 Java 中的 static { }赫编,其中的代碼有且只會(huì)被執(zhí)行一次巡蘸。

接著在 init{} 方法中篇裁,加載了 C/C++ 代碼編譯成的 so 庫: native-lib

往上一句代碼赡若,用 external 聲明了一個(gè)外部引用的方法 stringFromJNI() 达布,這個(gè)方法和 C/C++ 層的代碼是對(duì)應(yīng)的。

最終在最上面的 onCreate 中逾冬,將從 C/C++ 層返回的 String 顯示出來黍聂。

  • 第二,創(chuàng)建了一個(gè) cpp 文件包

其中有兩個(gè)文件非常重要身腻,分別是 native-lib.cpp 产还、 CMakeLists.txt

i. native-lib.cpp :是一個(gè) C++ 接口文件嘀趟,在 MainActivity 中聲明的外部方法將在這里得到實(shí)現(xiàn)脐区。

自動(dòng)生成 native-lib.cpp 的內(nèi)容如下:

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_chenlittleping_mynativeapp_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

可以看到,這個(gè) cpp 文件中的方法命名非常的長她按,不過其實(shí)非常簡單牛隅。

首先是頭部固定寫法 extern "C" JNIEXPORT jstring JNICALL

extern "C" 表示以 C語言 的方式來編譯;

jstring 表示該方法返回類型是 Java 層的 String 類型酌泰,類似的還是有: void jint等媒佣;

然后是 Java 層對(duì)應(yīng)方法的映射,即整個(gè)方法命名其實(shí)是 Java 層對(duì)應(yīng)方法的絕對(duì)路徑陵刹。

其中默伍,最前面的 Java_ 是固定寫法;

com_chenlittleping_mynativeapp_MainActivity_: 對(duì)應(yīng)的是 com.chenlittleping.mynativeapp.MainActivity.衰琐,其實(shí)就是 . 換為 _也糊;

stringFromJNI 和 Java 層的方法一致。

最后是兩個(gè)參數(shù)羡宙, JNIEnv *envjobject狸剃,分別代表 JNI 的上下文環(huán)境和調(diào)用這個(gè)接口的 Java 的類的實(shí)例。

調(diào)用這個(gè)方法辛辨,將會(huì)在 C++ 層創(chuàng)建一個(gè)字符串捕捂,并以 Java#String 的類型返回。

ii. CMakeLists.txt : 也就是構(gòu)建腳本斗搞。內(nèi)容如下:

# cmake 最低版本
cmake_minimum_required(VERSION 3.4.1)

# 配置so庫編譯信息
add_library( 
        # 輸出so庫的名稱
        native-lib

        # 設(shè)置生成庫的方式指攒,默認(rèn)為SHARE動(dòng)態(tài)庫
        SHARED

        # 列出參與編譯的所有源文件
        native-lib.cpp)

# 查找代碼中使用到的系統(tǒng)庫
find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# 指定編譯目標(biāo)庫時(shí),cmake要鏈接的庫
target_link_libraries(
        # 指定目標(biāo)庫僻焚,native-lib 是在上面 add_library 中配置的目標(biāo)庫
        native-lib

        # 列出所有需要鏈接的庫
        ${log-lib})

這是最簡單的編譯配置允悦,具體見上面的注釋。

CMakeLists.txt 的目的就是配置可以編譯出 native-lib so 庫的構(gòu)建信息虑啤。

說白了隙弛,就是告訴編譯器:

- 編譯的目標(biāo)是誰
- 依賴的源文件在哪里找
- 依賴的 `系統(tǒng)或第三方` 的 `動(dòng)態(tài)或靜態(tài)` 庫在哪里找架馋。
  • 第三,在 Gradle 文件中注冊(cè) CMake 腳本

第二步 中全闷,已經(jīng)把構(gòu)建 so 庫的信息配置好了叉寂,接下來要把這些信息注冊(cè)到 Gradle 中,編譯器才會(huì)去編譯它总珠。

app 的 build.gradle 內(nèi)容如下:

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 28
    buildToolsVersion "29.0.1"
    defaultConfig {
        applicationId "com.chenlittleping.mynativeapp"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        
        // 1) CMake 編譯配置
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    
    // 2) 配置 CMakeLists 路徑
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
}

dependencies {
    // 省略無關(guān)代碼
    //......
}

最主要的兩個(gè)地方是兩個(gè) externalNativeBuild 屏鳍。

第 1 個(gè) externalNativeBuild 中,可以做一些優(yōu)化配置局服,比如只打包包含 armeabi 架構(gòu)的 so

externalNativeBuild {
    cmake {
        cppFlags ""
    }
    ndk {
        abiFilters  "armeabi" //, "armeabi-v7a"
    }
}

第 2 個(gè) externalNativeBuild钓瞭,主要是配置 CMakeLists.txt 的路徑和版本。

Android Studio 為我們生成的關(guān)于 C/C++ 支持的主要就是以上三個(gè)地方淫奔,有了以上配置山涡,就可以在 MainActivity 頁面中正常的顯示出 Hello from C++

3) 在已有工程上添加 C/C++ 支持

前面就說過唆迁,在已有項(xiàng)目上添加 C/C++ 支持鸭丛,就是由我們自己手動(dòng)添加整個(gè)配置。那么根據(jù)簽名介紹的三個(gè)步驟媒惕,依葫蘆畫瓢系吩,就可以添加了。

這里剛好就用添加 FFMpeg so 到本系列文章現(xiàn)有 Demo 工程中來演示一遍妒蔚。

二、引入 FFmpeg so

1. 新建 cpp 目錄

首先月弛,在 app/src/main/ 目錄下肴盏,新建文件夾,并命名為 cpp 帽衙。

接著菜皂,在 cpp 目錄下,右鍵 New -> C/C++ Source File 厉萝,新建 native-lib.cpp 文件恍飘。

接著,在 cpp 目錄下谴垫,右鍵 New -> File 章母,新建 CMakeLists.txt ,先將上面 IDE 生成的那份代碼粘貼進(jìn)來翩剪, FFmpeg的配置在后面詳細(xì)講解乳怎。

# CMakeLists.txt

# cmake 最低版本
cmake_minimum_required(VERSION 3.4.1)

# 配置so庫編譯信息
add_library( 
        # 輸出so庫的名稱
        native-lib

        # 設(shè)置生成庫的方式,默認(rèn)為SHARE動(dòng)態(tài)庫
        SHARED

        # 列出參與編譯的所有源文件
        native-lib.cpp)

# 查找代碼中使用到的系統(tǒng)庫
find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# 指定編譯目標(biāo)庫時(shí)前弯,cmake要鏈接的庫
target_link_libraries(
        # 指定目標(biāo)庫蚪缀,native-lib 是在上面 add_library 中配置的目標(biāo)庫
        native-lib

        # 列出所有需要鏈接的庫
        ${log-lib})

2. 將 CMakeLists 配置到 build.gradle 中

android {
    // ...
    
    defaultConfig {
    // ...
    
    // 1) CMake 編譯配置
    externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    
    // ...
    
    // 2) 配置 CMakeLists 路徑
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
}

// ...

如果只是簡單的編寫 C/C++ 代碼秫逝,以上基礎(chǔ)配置就可以了。

接著來看看本文的重點(diǎn)询枚,如何使用 CMakeLists.txt 引入 FFmpeg 的動(dòng)態(tài)庫违帆。

3. 將 FFmpeg so 庫放到對(duì)應(yīng)的 CPU 架構(gòu)目錄

上一篇文章中,我們編譯的 FFmpeg so 庫的 CPU 架構(gòu)為 armv7-a金蜀,所以前方,我們需要把所有的 so 庫放置到 armeabi-v7a 目錄下。

首先廉油,在 app/src/main/ 目錄下惠险,新建文件夾,并命名為 jniLibs 抒线。

app/src/main/jniLibs 是 Android Studio 默認(rèn)的放置 so 動(dòng)態(tài)庫的目錄班巩。

接著,在 jniLibs 目錄下嘶炭,新建 armeabi-v7a 目錄抱慌。

最后把 FFmpeg 編譯得到的所有 so 庫粘貼到 armeabi-v7a 目錄。如下:

so目錄

4. 添加 FFmpeg so 的頭文件

在編譯 FFmpeg 的時(shí)候眨猎,除了生成 so 外抑进,還會(huì)生成對(duì)應(yīng)的 .h 頭文件,也就是 FFmpeg 對(duì)外暴露的所有接口睡陪。

FFmpeg編譯輸出

cpp 目錄下寺渗,新建 ffmpeg 目錄,然后把編譯時(shí)生成的 include 文件粘貼進(jìn)來兰迫。

頭文件目錄

5. 添加信殊、鏈接 FFmpeg so 庫

上面已經(jīng)把 so頭文件 放置到對(duì)應(yīng)的目錄中了,但是編譯器是不會(huì)把它們編譯汁果、鏈接涡拘、并打包到 Apk 中的,我們還需要在 CMakeLists.txt 中顯性的把相關(guān)的 so 添加和鏈接起來据德。完整的 CMakeLists.txt 如下:

cmake_minimum_required(VERSION 3.4.1)

# 支持gnu++11
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

# 1. 定義so庫和頭文件所在目錄鳄乏,方面后面使用
set(ffmpeg_lib_dir ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
set(ffmpeg_head_dir ${CMAKE_SOURCE_DIR}/ffmpeg)

# 2. 添加頭文件目錄
include_directories(${ffmpeg_head_dir}/include)

# 3. 添加ffmpeg相關(guān)的so庫
add_library( avutil
        SHARED
        IMPORTED )
set_target_properties( avutil
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libavutil.so )

add_library( swresample
        SHARED
        IMPORTED )
set_target_properties( swresample
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libswresample.so )
        
add_library( avcodec
        SHARED
        IMPORTED )
set_target_properties( avcodec
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libavcodec.so )
        
add_library( avfilter
        SHARED
        IMPORTED)
set_target_properties( avfilter
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libavfilter.so )
        
add_library( swscale
        SHARED
        IMPORTED)
set_target_properties( swscale
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libswscale.so )

add_library( avformat
        SHARED
        IMPORTED)
set_target_properties( avformat
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libavformat.so )

add_library( avdevice
        SHARED
        IMPORTED)
set_target_properties( avdevice
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libavdevice.so )

# 查找代碼中使用到的系統(tǒng)庫
find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log )

# 配置目標(biāo)so庫編譯信息
add_library( # Sets the name of the library.
        native-lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        native-lib.cpp
        )

# 指定編譯目標(biāo)庫時(shí),cmake要鏈接的庫        
target_link_libraries( 

        # 指定目標(biāo)庫棘利,native-lib 是在上面 add_library 中配置的目標(biāo)庫
        native-lib

# 4. 連接 FFmpeg 相關(guān)的庫
        avutil
        swresample
        avcodec
        avfilter
        swscale
        avformat
        avdevice

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib} )

主要看看注釋中新加入的 1~4 點(diǎn)橱野。

1)通過 set 方法定義了 so頭文件 所在目錄,方便后面使用赡译。

其中 CMAKE_SOURCE_DIR 為系統(tǒng)變量仲吏,指向 CMakeLists.txt 所在目錄。 ANDROID_ABI 也是系統(tǒng)變量,指向 so 對(duì)應(yīng)的 CPU 框架目錄:armeabi裹唆、armeabi-v7a誓斥、x86 ...

set(ffmpeg_lib_dir ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
set(ffmpeg_head_dir ${CMAKE_SOURCE_DIR}/ffmpeg)

2)通過 include_directories 設(shè)置頭文件查找目錄

include_directories(${ffmpeg_head_dir}/include)

3)通過 add_library 添加 FFmpeg 相關(guān)的 so 庫,以及 set_target_properties 設(shè)置 so 對(duì)應(yīng)的目錄许帐。

其中劳坑,add_library 第一個(gè)參數(shù)為 so 名字,SHARED 表示引入方式為動(dòng)態(tài)庫引入成畦。

add_library( avcodec
        SHARED
        IMPORTED )
set_target_properties( avcodec
        PROPERTIES IMPORTED_LOCATION
        ${ffmpeg_lib_dir}/libavcodec.so )

4)最后距芬,通過 target_link_libraries 把前面添加進(jìn)來的 FFMpeg so 庫都鏈接到目標(biāo)庫 native-lib 上。

這樣循帐,我們就將 FFMpeg 相關(guān)的 so 庫都引入到當(dāng)前工程中了框仔。下面就要來測(cè)試一下,是否可以正常調(diào)用到 FFmpeg 相關(guān)的方法了拄养。

三离斩、使用 FFmpeg

要檢查 FFmpeg 是否可以使用,可以通過獲取 FFmpeg 基礎(chǔ)信息來驗(yàn)證瘪匿。

1. 在 FFmpegAcrtivity 中添加一個(gè)外部方法 ffmpegInfo

把獲取到的 FFmpeg 信息顯示出來跛梗。

class FFmpegActivity: AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_ffmpeg_info)

        tv.text = ffmpegInfo()
    }

    private external fun ffmpegInfo(): String

    companion object {
        init {
            System.loadLibrary("native-lib")
        }
    }
}

2. 在 native-lib.cpp 中添加對(duì)應(yīng)的 JNI 層方法

#include <jni.h>
#include <string>
#include <unistd.h>

extern "C" {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavfilter/avfilter.h>
    #include <libavcodec/jni.h>

    JNIEXPORT jstring JNICALL
    Java_com_cxp_learningvideo_FFmpegActivity_ffmpegInfo(JNIEnv *env, jobject  /* this */) {

        char info[40000] = {0};
        AVCodec *c_temp = av_codec_next(NULL);
        while (c_temp != NULL) {
            if (c_temp->decode != NULL) {
                sprintf(info, "%sdecode:", info);
                switch (c_temp->type) {
                    case AVMEDIA_TYPE_VIDEO:
                        sprintf(info, "%s(video):", info);
                        break;
                    case AVMEDIA_TYPE_AUDIO:
                        sprintf(info, "%s(audio):", info);
                        break;
                    default:
                        sprintf(info, "%s(other):", info);
                        break;
                }
                sprintf(info, "%s[%10s]\n", info, c_temp->name);
            } else {
                sprintf(info, "%sencode:", info);
            }
            c_temp = c_temp->next;
        }
        return env->NewStringUTF(info);
    }
}

首先,我們看到代碼被包裹在 extern "C" { } 當(dāng)中棋弥,和前面的系統(tǒng)創(chuàng)建的稍微有些不同核偿,通過這個(gè)大括號(hào)包裹,我們就不需要每個(gè)方法都添加單獨(dú)的 extern "C" 開頭了顽染。

另外漾岳,由于 FFmpeg 是使用 C 語言編寫的,所在 C++ 文件中引用 #include 的時(shí)候家乘,也需要包裹在 extern "C" { }蝗羊,才能正確的編譯。

方法的新建就不用說了仁锯,和前面介紹的命名方法一致。

在方法中翔悠,使用 FFmpeg 提供的方法 av_codec_next业崖,獲取到 FFmpeg 的編解碼器,然后通過循環(huán)遍歷蓄愁,將所有的音視頻編解碼器信息拼接起來双炕,最后返回給 Java 層。

至此撮抓,FFmpeg 加入到工程中妇斤,并被調(diào)用。

如果一切正常,App運(yùn)行后站超,就會(huì)顯示出 FFmpeg 音視頻編解碼器的信息荸恕。

如果由提示 so 或者 頭文件 找不到,需要檢查 CMakeLists.txt 中設(shè)置的 so頭文件 的路徑是否正確死相。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載融求,如需轉(zhuǎn)載請(qǐng)通過簡信或評(píng)論聯(lián)系作者。
  • 序言:七十年代末算撮,一起剝皮案震驚了整個(gè)濱河市生宛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肮柜,老刑警劉巖陷舅,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異审洞,居然都是意外死亡莱睁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門预明,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缩赛,“玉大人,你說我怎么就攤上這事撰糠∷肘桑” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵阅酪,是天一觀的道長旨袒。 經(jīng)常有香客問我,道長术辐,這世上最難降的妖魔是什么砚尽? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮辉词,結(jié)果婚禮上必孤,老公的妹妹穿的比我還像新娘。我一直安慰自己瑞躺,他們只是感情好敷搪,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著幢哨,像睡著了一般赡勘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捞镰,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天闸与,我揣著相機(jī)與錄音毙替,去河邊找鬼。 笑死践樱,一個(gè)胖子當(dāng)著我的面吹牛厂画,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播映胁,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼木羹,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了解孙?” 一聲冷哼從身側(cè)響起坑填,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弛姜,沒想到半個(gè)月后脐瑰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡廷臼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年苍在,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荠商。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寂恬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出莱没,到底是詐尸還是另有隱情初肉,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布饰躲,位于F島的核電站牙咏,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嘹裂。R本人自食惡果不足惜妄壶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望寄狼。 院中可真熱鬧丁寄,春花似錦、人聲如沸泊愧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拼卵。三九已至,卻和暖如春蛮艰,著一層夾襖步出監(jiān)牢的瞬間腋腮,已是汗流浹背雀彼。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留即寡,地道東北人徊哑。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子擦秽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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