Android音频框架笔记 - 上篇

提醒一下,纯个人笔记,你完全可能看晕

一、音频数字化基础知识

见书,列出知识点如下:

  • 声音
    声波,声音频率、响度, 音调、音色。
    音响设备中的声道
    得翻翻初高中的课本了。

  • 声音数字化过程
    声源 -> mic -> ADC(模-数转换器) ,采样、量化 -> [可选项] 过滤、音效等特殊化处理 -> 编码 ->
    其中采样过程,涉及采样率、采样深度、声道等。
    奈奎斯特采样定律: 见书。
    音量调节与韦伯定律: 见书。
    这得看看大学课本:高数、模拟电路、数字电路、多媒体技术。

  • 音频编码解码格式
    是指对原始PCM音频数据的压缩与编码、解码格式。

  • 音频文件格式
    是指存储音频数据的文件存储格式。

二、架构层级图

音频框架全图

Paste_Image.png

Paste_Image.png

Paste_Image.png

三、代码与生成的库

3-1、代码位置

frameworks\av\media
\frameworks\av\services\audioflinger
frameworks/base/media/

Audio 系统代码

  • 1、Audio 的Java 部分
    frameworks/base/media/java/android/media
    frameworks/base/media/jni
    生成framework.jar,media是里面的一部分。

与Audio 相关的Java包是android.media,主要包含AudioManager和Audio 系统的几个类。
提供了两套API,如:
AudioTrack.java/AudioRecord.java 更原始,更灵活,也更不方便,
MediaPlayer/MediaRecord包含了编解码,文件格式支持,更简便。

  • 2、Audio 的JNI 部分

frameworks/base/core/jni
生成库libandroid_runtime.so,Audio 的JNI是其中的一个部分。这部分屏蔽了对Audio本地框架调用细节,简化后,相当于JAVA接口的本地中转,本地接口转换本来就是JNI功能之一。

  • 3、Audio 的框架部分

总位置frameworks\av\media,里面都是多媒体相关的

3.1
libmedia.so
部分,Audio系统的核心框架,向上层Audio 的
JNI 部分提供接口,间接为Audio的Java部分提供接口。
frameworks\av\media\libmedia
frameworks\av\include\media
旧版本:
frameworks/base/include/media/
frameworks/base/media/libmedia/

这部分内容被编译成库libmedia.so,实现Audio系统的核心框架,同时提供了IAudioFlinger 类接口。在这个类中,可以获得IAudioTrack 和IAudioRecorder 两个接口,分别用于声音的播放和录制。
AudioTrack 和AudioRecorder
分别调用IAudioTrack 和IAudioRecorder 来实现。IAudioFlinger.h、IAudioTrack.h 和IAudioRecorder.h 这三个接口通过下层来实现。AudioSystem.h、AudioTrack.h 和AudioRecorder.h 是对上层提供的接口,它们既供本地程序调用,也可以通过JNI 向Java 层提供接口。从功能上看,
AudioSystem
负责的是Audio 系统的综合管理功能,而AudioTrack 和AudioRecorder 分别负责音频数据的输出和输入,即播放和录制。另外一个接口是IAudioFlingerClient,它作为向IAudioFlinger中注册的监听器,相当于使用回调函数获取IAudioFlinger运行时信息。

MediaScanner
负责媒体文件扫描。

3.2 mediaplayerservice
frameworks\av\media\libmediaplayerservice
生成 libmediaplayerservice.so

上层其中一套API的服务端实现,即MediaPlayer/MediaRecord的服务端实现。
其实最终实现,Audio部分是通过libmedia.so里的AudioTrack/AudioRecord实现。
此外,内部包括了编码、解码、文件格式、视频播放/录制等。
视频播放部分,有好几个播放器,如StagefrightPlayer/Recorder,NuPlayer,而早期还有用openCore。

3.3 mediaserver,媒体服务进程
frameworks\av\media\mediaserver
所有媒体服务端代码线程都在此进程被启动
生成/system/bin/mediaserver

3.4 libeffects 音效部分
frameworks\av\media\libeffects
生成
/system/lib/libeffects.so // 总入口
/system/lib/soundfx/ //具体各种音效

  • 4、本地services
    部分,音频服务进程

4.1 Audio Flinger
frameworks\av\services\audioflinger
旧版
frameworks/base/libs/audioflinger
这部分内容被编译成库libaudioflinger.so,它是Audio系统的本地服务部分。(其实也有libaudioresampler.so)

4.2 audiopolicy本地框架服务部分
frameworks\av\services\audiopolicy
生成libaudiopolicymanager.so, libaudiopolicyservice.so , libaudiopolicymanagerdefault.so

音频策略的本地服务框架,
向上提供接口,即向AudioSystem.cpp/AudioService.cpp/AudioManager.cpp等提供接口,//我猜是这样,至少AudioSystem.cpp确实包装了AudioPolicyService.cpp
向下通过HAL层调用到厂商真正实现音频策略的库,默认实现为audio_policy.default.so,而最终又调用到具体实现类AudioPolicyManagerBase。
功能上只负责策略性处理,包括转给audio_policy.default.so后得到的结果,
而具体音频处理事务转到AudioFlinger最终去执行。
关键点是AudioPolicyManagerBase 会去加载audio_policy.conf , audio_effects.conf,从而实现音频设备、音效上的策略的可配置。所以一般只改配置,应该很少有厂商去改AudioManagerBase吧。

  • 5、Audio 的硬件抽象层接口
    Android对Audio HAL的框架定义及遗留实现
    hardware\libhardware\modules
    hardware/libhardware_legacy/include/hardware/

HAL厂商实现代码
device\samsung\manta\audio
device\asus\grouper\audio

1、Audio使用JNI和Java对上层提供接口,JNI部分通过调用libmedia库提供的接口来实现。
2、 Audio 本地框架类是libmedia.so的一个部分,这些Audio框架类对上层提供接口,由下层的本地代码去实现。
3、AudioFlinger继承libmeida中的接口,提供实现库libaudiofilnger.so。这部分内容没有自己的对外头文件,上层调用的只是libmedia本部分的接口,但实际调用的内容是libaudioflinger.so。
4、Audio的硬件抽象层提供到硬件的接口,供AudioFlinger调用。Audio的硬件抽象层实际上是各个平台开发过程中需要主要关注和独立完成的部分。
在Android的Audio系统中,无论上层还是下层,都使用一个管理类和输出输入两个类来表示整个Audio系统,输出输入两个类负责数据通道。在各个层次之间具有对应关系:

Paste_Image.png
  • 6、底层音频架构相关**
    system\core\include\system
    external/tinyalsa
    或 alsa版本
    external/alsa-lib
    external/alsa-utils

4.1后基本都用tinyalsa

  • 7、其它
    7.1 音频常量定义
    system\core\include\system\audio.h
    AudioSystem.java
    里面很多常量保持一致,一个给native引用,一个给java引用。

7.2 native层公用头文件定义
system\core\include\system\audio.h
这个头文件很重要,它的一些常量定义,需要上下均保持一致,所以Java层中的AudioSystem.java与此保持一致。
audio.h则是给native层的所有模块引用,
包括JNI层的各cpp文件,libaudioflinger.so,audio.xxx.so,audio_policy.so等。
(当然,废话一句,到alsalib库就不是了,它引用头文件是alsa标准的头文件,旨在按alsa架构看齐,
并向上提供统一的alsalib接口,反而是Android的HAL层要使用alsalib的头文件。)

音频库与代码位置

libmedia.so
frameworks\av\media\libmedia
音频服务框架代码
libaudioflinger.so****
\frameworks\av\services\audioflinger
HAL框架代码
hardware\libhardware\modules
HAL的官方标准实现代码?
HAL厂商实现代码
device\samsung\manta\audio

device\asus\grouper\audio
底层音频架构相关
system\core\include\system
external/tinyalsa

音频库与小机文件位置

音频服务框架库:** libaudioflinger.so libmedia.so
一般放在机器中的/system/lib目录。
音频设备HAL库:audio_xxx.so
具体由硬件厂商来实现和定制,audio_policy.conf的配置跟这些实现要对应。
这些库即音频HAL层框架的实现,音频HAL框架定义在:hardware\libhardware\include\hardware\audio.h
具体的实现代码需要厂商提供,而google默认的实现,代码在: ???
生成的库,一般会有:audio_primary.so,audio_a2dp.so等。
一般放在机器中的/system/lib/hw或vendor/lib/hw目录。

相关进程:

systemserver: AudioFlinger, AudioPolicyService等,即Audio服务端
APP所在进程:AudioTrack,AudioRecoder等,即Audio客户端

andorid.process.media: 多媒体管理类所在进程

3-2、运行时模型,进程线程关系

四、Linux中的音频框架

  • Linux原生三种:OSS, ALSA, TinyAlsa。
    目前Android使用的是TinyALSA音频库。TinyAlsa其实只是位于Linux应用层的一个音频库,依赖于ALSA驱动架构,即底层驱动必须是用ALSA驱动架构,它是Android用于替代ALSA架构在Linux应用层的庞大复杂的库,让其更适用于嵌入式设备。
    反正,它为Linux应用层提供了统一方便的音频接口。如pcm_read() / pcm_write()
    而在Linux内核中,使用的ALSA音频架构,驱动框架按ALSA框架接口来写,驱动那块的移植是SOC厂商的事情。

参考:
ALSA官方文档
http://www.alsa-project.org/~tiwai/writing-an-alsa-driver/index.html
ALSA声卡驱动系列文章
http://blog.csdn.net/DroidPhone/article/category/1118446
各种音频硬件通路图
http://blog.csdn.net/qianjin0703/article/details/6387662
拼凑还算完整的一篇(部分来自以上的文章)
http://www.360doc.com/content/14/0414/10/97538_368733041.shtml

  • 支持的音频设备(输出)列表
    硬件音频输出设备的类型, 见书

在HAL层,一个音频设备,用一个 audio_hw_device_t 结构体来表示,而它支持的输出类型,在audio_policy.conf被指明。
当上层要打开一个OUT通道时,会寻找匹配的类型,进行打开。

  • Android支持的媒体格式
    见书
    可录制的音频来源
    可录制的视频来源
    CAMERA 或 DEFAULT

五、代码详解

5-1、 类的使用

AudioRecord.cpp / AudioRecord.java(只是前者的JAVA封装)

AudioRecord.java的使用步骤

  1. 创建一个数据流。
  2. 构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小可以通过getMinBufferSize方法得到。如果buffer容量过小,将导致对象构造的失败。
  3. 初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小。
  4. 开始录音。
  5. 从AudioRecord中读取声音数据到初始化buffer,将buffer中数据导入数据流。
  6. 停止录音。
  7. 关闭数据流。

对应的AudioRecord.cpp的使用代码:

AudioRecord  pAudioRecord  = new android::AudioRecord();    
pAudioRecord->set( inputSource,
                                sampleRateInHz,
                                audioFormat,
                                channelConfig,
                                minFrameCount,
                                AudioRecordCallback,
                                NULL,
                                0,
                                true,
                                0,
 android::AudioRecord::TRANSFER_DEFAULT, 
 AUDIO_INPUT_FLAG_NONE, //AUDIO_INPUT_FLAG_NONE //audio_input_flags_t flags);
 NULL
 ); 

AudioRecord recordInstance = new AudioRecord(
                MediaRecorder.AudioSource.REMOTE_SUBMIX, frequency,
                AudioFormat.CHANNEL_IN_MONO, audioEncoding, bufferSize);
recordInstance.startRecording();

//AudioRecod.cpp中的set()函数,看来一些限制:

AudioRecod.set(){
  if (inputSource == AUDIO_SOURCE_DEFAULT) {** //默认录制音源,即是MIC
       inputSource = AUDIO_SOURCE_MIC; 
    }
if (sampleRate == 0) {
    ALOGE("Invalid sample rate %u", sampleRate);
    return BAD_VALUE;
}

// these below should probably come from the audioFlinger too...
if (format == AUDIO_FORMAT_DEFAULT) {**//默认数据格式,即是**AUDIO_FORMAT_PCM_16_BIT
    format = AUDIO_FORMAT_PCM_16_BIT;
}

// Temporary restriction: AudioFlinger currently supports 16-bit PCM only
if (format != AUDIO_FORMAT_PCM_16_BIT) {**//数据格式,其实只能是**AUDIO_FORMAT_PCM_16_BIT
    ALOGE("Format %#x is not supported", format);
    return BAD_VALUE;
}

}

一些问题:
有哪些音源,输入还是输出? 单双声道? 音源和后两者的参数,如何配对?

其中:
录制音源,*CPP中与JAVA层定义一致。有如下一些音源。
总结下,就是MIC,通话进出,REMOTE_SUBMIX,CAMCORDER(录像用的mic,一般没有独立,最终其实也是默认的MIC)。
所以有些东西还是录制不到的,比如主设备混音后输出的声音,这包括了各种可能的音乐、铃声、通知等声音。
也就是,Android给到上层应用的接口是,只能给你录制MIC,录制通话,录像。其余录音场景,不给。
typedef enum {
AUDIO_SOURCE_DEFAULT = 0,
AUDIO_SOURCE_MIC = 1,
AUDIO_SOURCE_VOICE_UPLINK = 2,
AUDIO_SOURCE_VOICE_DOWNLINK = 3,
AUDIO_SOURCE_VOICE_CALL = 4,
AUDIO_SOURCE_CAMCORDER = 5,
AUDIO_SOURCE_VOICE_RECOGNITION = 6,
AUDIO_SOURCE_VOICE_COMMUNICATION = 7,
AUDIO_SOURCE_REMOTE_SUBMIX = 8, /
Source for the mix to be presented remotely. /
/
An example of remote presentation is Wifi Display /
/
where a dongle attached to a TV can be used to /
/
play the mix captured by this audio source. /
AUDIO_SOURCE_CNT,
AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1,
AUDIO_SOURCE_HOTWORD = 1999, /
A low-priority, preemptible audio source for
for background software hotword detection.
Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION.
Used only internally to the framework. Not exposed
at the audio HAL. */
} audio_source_t;

声道:
定义了输入和输出,分别又定义了单、双、前后、5.1声道、7.1声道的各个声道,然后又定义了更方便使用的各种组合声道。
意思是按人们对于声道的理解来定义,而不理会底层如何实现。
显然,有些音源,是不可能有5.1声道的,对吧,所以,还是有个有效匹配问题。

AudioTrack

见书,

  • 简单说来,播放流程如下:
    Java端发起调用,MediaPlayer会转至MediaPlayerService,然后会调用相应的解码工具解码后创建AudioTrack,所有待输出的AudioTrack在AudioFlinger::AudioMixer里合成,然后通过AudioHAL(AudioHardwareInterface的实际实现者)传至实际的硬件来实现播放。

如果直接使用AudioTrack.java-> AudioTrack.cpp, 则后续流程直接到AudioFlinger,然后往下。
AudioTrack总结
通过这一次的分析,我自己觉得有以下几个点:
l AudioTrack的工作原理,尤其是数据的传递这一块,做了比较细致的分析,包括共享内存,跨进程的同步等,也能解释不少疑惑了。
l 看起来,最重要的工作是在AudioFlinger中做的。通过AudioTrack的介绍,我们给后续深入分析AudioFlinger提供了一个切入点
工作原理和流程嘛,再说一次好了,JAVA层就看最前面那个例子吧,实在没什么说的。
l AudioTrack被new出来,然后set了一堆信息,同时会通过Binder机制调用另外一端的AudioFlinger,得到IAudioTrack对象,通过它和AudioFlinger交互。
l 调用start函数后,会启动一个线程专门做回调处理,代码里边也会有那种数据拷贝的回调,但是JNI层的回调函数实际并没有往里边写数据,大家只要看write就可以了
l 用户一次次得write,那AudioTrack无非就是把数据memcpy到共享buffer中咯
l 可想而知,AudioFlinger那一定有一个线程在memcpy数据到音频设备中去。我们拭目以待。

5-2、相关类说明

  • 多媒体管理类
    MediaScannerService
    三种广播的扫描流程
    三种广播的扫描路径
    MediaProvider
    MediaReceiver
    MediaStore

  • 播放与录制,JAVA层
    MediaPlayer
    封装给App层,用于播放音视频多媒体的类
    状态迁移图
    框架图
    MediaRecorder
    封装给App层,用于录制音视频多媒体的类。非常友好的封装,录制、编码、存储等所有细节都被封装起来。****
    状态迁移图
    框架图

AudioRecord.java
封装给APP层的音频录制类,提供非常友好,简化的接口和调用流程。
通过JNI封装了AudioRecord.cpp功能类的具体事务操作。
JNI文件:android_media_AudioRecord.cpp

AudioTrack.java
AudioTrack.cpp的java层封装类,播放音频

AudioTrack.cpp
用于音频数据的回放。
分两类数据传送模式:
主动push 和 被pull
分两类数据操作模式:
static
数据量小,时间短,如铃音、通知、音效。数据一次性交付,然后播放。
stream
数据量大,时间长,如音乐等。数据通过共享内存方式传送,以流形式传输播放。****

AudioRecord.cpp
音频录制功能类,client端,服务端才是真正执行录制工作的。
AudioRecord:****AudioRecordThread

  • 框架层
    MediaPlayerService
    APP层MediaPlayer和MediaRecorder的服务端,播放和录制操作的服务类,向下调用真正播放和操作的执行类与功能类。
    MediaRecoderClient
    与客户端MediaRecorde****r对应的服务端的助理类,多实例,一一对应关系。
    StageFrightRecoder
    直接的录制器,录制操作的执行类。
    Client
    与客户端MediaPlaye****r对应的服务端的助理类,多实例,一一对应关系。****
    各种播放器类
    类型有:NU_PLAYER, PV_PLAYER等

AudioSystem.cpp
包装类,隔离AudioTrack/AudioRecord类 <==> AudioPolicyService、AudioFlinger。
因此向上提供统一接口,隔离后者策略类、音频服务类的变化。
看其实现,就是中转调用,转给AudioPolicyService或AudioFlinger。

AudioService

AudioFlinger
AudioFlinger(下面简称AF)是整个音频系统的核心与难点。作为Android系统中的音频中枢,它同时也是一个系统服务,启到承上(为上层提供访问接口)启下(通过HAL来管理音频设备)的作用。AudioFlinger向下访问AudioHardware,实现输出音频数据,控制音频参数。
AudioFlinger则是策略的执行者,例如具体如何与音频设备通信,如何维护现有系统中的音频设备,以及多个音频流的混音如何处理等等都得由它来完成。
AudioFlinger-->PlaybackThread
负责回放音频数据工作的线程类
**AudioFlinger-->MixerThread
负责混音工作的线程类
**AudioFlinger-->MixerThread-->AudioMixer
负责混音具体事务的功能类

@ AudioFlinger的创建与初始化
创建见书,
TODO: 初始化

@ 对音频设备的管理
AudioPolicyManagerBase 构造函数中,AudioPolicyManagerBase->loadAudioPolicyConfig,对 audio_policy.conf加载、解析, 找出可用的音频设备 -> AudioFlinger加载模块

AudioPolicyService
AudioPolicyService是策略的制定者,比如什么时候打开音频接口设备、某种Stream类型的音频对应什么设备等等。而AudioFlinger则是策略的执行者,例如具体如何与音频设备通信,如何维护现有系统中的音频设备,以及多个音频流的混音如何处理等等都得由它来完成。AudioPolicyService根据用户配置来指导AudioFlinger加载设备接口,起到路由功能。

audio_policy.conf
不同的Android产品在音频(设备)的设计上通常是存在差异的,而这些差异可以同过Audio的配置文件audio_policy.conf来获得。在Android系统中音频配置文件存放路径有两处,存放地址可以从AudioPolicyManagerBase.cpp文件中知道:

 #define AUDIO_POLICY_VENDOR_CONFIG_FILE  "/vendor/etc/audio_policy.conf"
 #define AUDIO_POLICY_CONFIG_FILE         "/system/etc/audio_policy.conf"

在AudioPolicyManager.cpp文件中可以知道系统会首先加载vendor/etc目录下的configure文件,再加载system/etc目录下的configure文件。若这两者加载都发生错误的话,系统会加载default配置文件,并命名为primary module,从这可以看出,音频系统中一定必须存在的module就是primary了。

TODO: 这些个配置,什么来由,在代码中如何加载,处理,生成对应的audio_hw_device_t,上层如何来用这些信息,干嘛用。
在HAL层,一个音频设备,用一个 audio_hw_device_t 结构体来表示,而它支持的输出类型,在audio_policy.conf被指明。
当上层要打开一个OUT通道时,会寻找匹配的类型,进行打开。

audio_effects.conf
音效配置文件,最终机器,一般存放在:
/system/etc/audio_effects.conf
/vendor/etc/audio_effects.conf

struct default_audio_policy {
struct audio_policy policy;

struct audio_policy_service_ops *aps_ops;
void *service;
};

AudioPolicyManagerBase
是什么,干什么用?
初始化过程: AudioPolicyService构造中: rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,
&mpAudioPolicy); -> .... -> AudioPolicyManagerBase

@ Audio策略类的路由功能?
何谓路由功能,路由过程是怎样的?
audio.h中的输出设备类型是怎么回事? 与audio_policy.conf有什么关系?
policy类是如何使用这些定义及配置的?
最终如何选择到合适的输出设备,即对应audio_deivce类,找到对应输出通道,即audio_deivce ->openOutputStream,具体打开哪一个?
Android上层定义的声音流类型,又是如何对应下来的?
**据说上面调用下来,辗转libaudiopolicy.so 后,到AudioPolicyManagerBase.getDevicesForStream()->****.getDeviceForStrategy(), 里面正是AudioSystem.java/AudioSystemLegacy.h中定义的声音流类型与audio.h中定义的音频设备类型转换过程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容