音視頻開發(fā)之旅(四)Camera視頻采集

目錄

  1. Camera基礎知識
  2. 視頻采集的流程
  3. 遇到的問題和常見的坑(重點)
  4. 收獲

一窟赏、 Camera基礎知識

Camera 有幾個重要的基礎概念枫弟。

  1. facing相機的方向究反,一般后置攝像頭和前置攝像頭慈参。

  2. Orientation:相機采集圖片的角度窍帝,攝像頭的傳感器在手機中是橫向的榕茧,在預覽的時候根據(jù)Camera的預覽方向進行順時針旋轉對應角度進行設置即可正常預覽垃沦。如果不正確設置會導致預覽時出現(xiàn)倒立、鏡像等問題用押。把預覽的圖片保存為相冊也要單獨設置方向肢簿,注意這個方向和預覽方向互不相干。

  3. 預覽圖片的大小 預覽容器的大小和攝像頭支持的圖片預覽的圖片大小蜻拨,如果設置了Camera不支持的預覽大小池充,會導致黑屏。

  4. 可以設置幀回調然后缎讼,在每一幀中進行業(yè)務處理收夸,比如,人臉識別等功能

  5. Camera預覽的圖片格式有NV21 YUV420sp等

  6. Camera需要一個容器把的Surface顯示在屏幕上血崭,一般SurfaceView卧惜,TextureView等。

二夹纫、視頻采集的流程

  1. 通過SurfaceView拿到SurfaceHolder咽瓷,然后設置addCallback回調,當Surface創(chuàng)建舰讹、銷毀茅姜、改變時觸發(fā)對應的回調,在其中可以進行Camera的初始化以及參數(shù)設置

  2. 通過new Camera(cameralId)生成一個對象月匣。然后Camera.getParams獲取到相關的參數(shù)钻洒,可以把重要的或者說比較關系的parmas打印出來,比如說支持多少個攝像頭锄开、支持的預覽圖片的大小素标、每個攝像頭的方向等信息∑笺玻可以根據(jù)需要設置對應的參數(shù)糯钙,比如圖片的格式粪狼、圖片的預覽大小等。當然有一個必須要設置的就是Camera的預覽展示方向任岸,否則預覽的到圖片和正常的方向不一致再榄。

  3. 可以設置Camera的setPreviewCallback獲取每一幀的回調,根據(jù)需要設置處理享潜,開始預覽startPreview以及幀回調的處理

  4. 攝像頭的切換

如果出發(fā)Camera的切換困鸥,需要把前一個Camera釋放,重新生成和設置Camera切換預覽
和Activity生命周期想的關系剑按,這個是有SurfaceView決定的疾就,當頁面可見時(onResume)創(chuàng)建或重新創(chuàng)建,頁面不可見時(onPause)銷毀釋放

具體實現(xiàn)如下

  1. SurfaceView的設置
private SurfaceHolder mSurfaceHolder;

    private void initSurfaceView() {
        mSurfaceHolder = surfaceview.getHolder();
        mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                Log.d(TAG, "surfaceCreated: ");
                handleSurfaceCreated();
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                Log.d(TAG, "surfaceDestroyed: ");
                handleSurfaceDestroyed();
            }
        });
    }

    private void handleSurfaceDestroyed() {
        releaseCamera();
        mSurfaceHolder = null;
        Log.i(TAG, "handleSurfaceDestroyed: ");
    }

    private void handleSurfaceCreated() {
        Log.i(TAG, "handleSurfaceCreated: start");
        if (mSurfaceHolder == null) {
            mSurfaceHolder = surfaceview.getHolder();
        }
        if (mCamera == null) {
            initCamera(curCameraId);
        }
        try {
            //問題2:頁面重新打開后SurfaceView的內容黑屏
            //Camera is being used after Camera.release() was called
            //在surfaceDestroyed時調用了Camera的release 但是沒有設置為null,
            //--》如何解耦合艺蝴,把生命周期相關的方法和Camera的生命周期綁定而不時在回調中處理猬腰,方便業(yè)務實現(xiàn)
            //onResume--》surfaceCreated
            //onPause--》surfaceDestroyed
            mCamera.setPreviewDisplay(mSurfaceHolder);
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG, "handleSurfaceCreated: " + e.getMessage());
        }
        startPreview();
        Log.i(TAG, "handleSurfaceCreated: end");
    }

    private void startPreview() {
//        mCamera.setPreviewCallback(new Camera.PreviewCallback() {
//            @Override
//            public void onPreviewFrame(byte[] data, Camera camera) {
//                Log.i(TAG, "onPreviewFrame: setPreviewCallback");
//            }
//        });
        //問題:很多時候,不僅僅要預覽猜敢,在預覽視頻的時候姑荷,希望能做一些檢測,比如人臉檢測等缩擂。這就需要獲得預覽幀視頻鼠冕,該如何做吶?
        mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
            @Override
            public void onPreviewFrame(byte[] data, Camera camera) {
                Log.i(TAG, "onPreviewFrame: setOneShotPreviewCallback");
                Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
                YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
                ByteArrayOutputStream os = new ByteArrayOutputStream(data.length);
                if(!yuvImage.compressToJpeg(new Rect(0,0,previewSize.width,previewSize.height),100,os)){
                    Log.e(TAG, "onPreviewFrame: compressToJpeg error" );
                    return;
                }
                byte[] bytes = os.toByteArray();
                Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

                //這里的處理方式是簡單的把預覽的一幀圖保存下胯盯。如果需要做人臉設別或者其他操作懈费,可以拿到這個bitmap進行分析處理
                //我們可以通過找出這張圖片發(fā)現(xiàn)預覽保存的圖片的方向是不對的,還是Camera的原始方向博脑,需要旋轉一定角度才可以憎乙。
                if(curCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
                    bitmap = BitmapUtils.rotate(bitmap,90);
                }else {
                    bitmap = BitmapUtils.mirror(BitmapUtils.rotate(bitmap,270));
                }
                FileUtils.saveBitmapToFile(bitmap,"oneShot.jpg");
            }
        });

//        mCamera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
//            @Override
//            public void onPreviewFrame(byte[] data, Camera camera) {
//                Log.i(TAG, "onPreviewFrame: setPreviewCallbackWithBuffer");
//            }
//        });
        mCamera.startPreview();

    }

2: Camera的初始化和Params設置

private void initCamera(int cameraId) {
        curCameraId = cameraId;
        mCamera = Camera.open(curCameraId);
        Log.d(TAG, "initCamera: Camera Open ");

        setCamerDisplayOrientation(this, curCameraId, mCamera);

        if (!hadPrinted) {
            printCameraInfo();
            hadPrinted = true;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        Camera.Size closelyPreSize = CameraUtil.getCloselyPreSize(true, SystemUtils.getDisplayWidth(), SystemUtils.getDisplayHeight(), parameters.getSupportedPreviewSizes());
        Log.i(TAG, "initCamera: closelyPreSizeW="+closelyPreSize.width+" closelyPreSizeH="+closelyPreSize.height);
        parameters.setPreviewSize(closelyPreSize.width, closelyPreSize.height);
        mCamera.setParameters(parameters);
    }

    private void printCameraInfo() {
        //1. 調用getParameters獲取Parameters
        Camera.Parameters parameters = mCamera.getParameters();

        //2. 獲取Camera預覽支持的圖片格式(常見的是NV21和YUV420sp)
        int previewFormat = parameters.getPreviewFormat();
        Log.d(TAG, "initCamera: previewFormat=" + previewFormat); // NV21

        //3. 獲取Camera預覽支持的W和H的大小,
        // 手動設置Camera的W和H時叉趣,要檢測camera是否支持寨闹,如果設置了Camera不支持的預覽大小,會出現(xiàn)黑屏君账。
        // 那么這里有一個問,由于Camera不同廠商支持的預覽大小不同沈善,如果做到兼容吶乡数?
        // 需要使用方采用一定策略進行選擇(比如:選擇和預設置的最接近的支持的WH)
        //通過輸出信息,我們可以看到Camera是橫向的即 W>H
        List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
        for (Camera.Size item : supportedPreviewSizes
        ) {
            Log.d(TAG, "initCamera: supportedPreviewSizes w= " + item.width + " h=" + item.height);
        }

        //可以看到Camera的寬高是屏幕的寬高是不一致的闻牡,手機屏幕是豎屏的H>W净赴,而Camera的寬高是橫向的W>H
        Camera.Size previewSize = parameters.getPreviewSize();
        int[] physicalSS = SystemUtils.getPhysicalSS(this);
        Log.i(TAG, "initCamera: w=" + previewSize.width + " h=" + previewSize.height
                + " screenW=" + SystemUtils.getDisplayWidth() + " screenH=" + SystemUtils.getDisplayHeight()
                + " physicalW=" + physicalSS[0] + " physicalH=" + physicalSS[1]);

        //4. 獲取Camera支持的幀率 一般是10~30
        List<Integer> supportedPreviewFrameRates = parameters.getSupportedPreviewFrameRates();
        for (Integer item : supportedPreviewFrameRates
        ) {
            Log.i(TAG, "initCamera: supportedPreviewFrameRates frameRate=" + item);
        }

        //5. 獲取Camera的個數(shù)信息,以及每一個Camera的orientation罩润,這個很關鍵玖翅,如果根據(jù)Camera的orientation正確的設置Camera的DisplayOrientation可能會導致預覽倒止或者出現(xiàn)鏡像的情況
        int numberOfCameras = Camera.getNumberOfCameras();
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        for (int i = 0; i < numberOfCameras; i++) {
            Camera.getCameraInfo(i, cameraInfo);
            Log.i(TAG, "initCamera: facing=" + cameraInfo.facing
                    + " orientation=" + cameraInfo.orientation);
        }
    }

    /**
     *
     * @param activity
     * @param cameraId
     * @param camera
     */
    public static void setCamerDisplayOrientation(Activity activity, int cameraId, Camera camera) {
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, cameraInfo);
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        Log.i(TAG, "setCamerDisplayOrientation: rotation=" + rotation + " cameraId=" + cameraId);
        int degress = 0;

        switch (rotation) {
            case Surface.ROTATION_0:
                degress = 0;
                break;
            case Surface.ROTATION_90:
                degress = 90;
                break;
            case Surface.ROTATION_180:
                degress = 180;
                break;
            case Surface.ROTATION_270:
                degress = 270;
                break;
        }
        int result = 0;
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (cameraInfo.orientation + degress) % 360;
            result = (360 - result) % 360;

        } else if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
            result = (cameraInfo.orientation - degress + 360) % 360;
        }
        Log.i(TAG, "setCamerDisplayOrientation: result=" + result + " cameraId=" + cameraId + " facing=" + cameraInfo.facing + " cameraInfo.orientation=" + cameraInfo.orientation);

        camera.setDisplayOrientation(result);
    }
  1. Camera的預覽站削、幀回調處理田炭、保存圖片旋轉和鏡像處理
private void startPreview() {

        //問題六:很多時候,不僅僅要預覽,在預覽視頻的時候狮崩,希望能做一些檢測,比如人臉檢測等缨叫。這就需要獲得預覽幀視頻
        mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
            @Override
            public void onPreviewFrame(byte[] data, Camera camera) {
                Log.i(TAG, "onPreviewFrame: setOneShotPreviewCallback");
                Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
                YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
                ByteArrayOutputStream os = new ByteArrayOutputStream(data.length);
                if(!yuvImage.compressToJpeg(new Rect(0,0,previewSize.width,previewSize.height),100,os)){
                    Log.e(TAG, "onPreviewFrame: compressToJpeg error" );
                    return;
                }
                byte[] bytes = os.toByteArray();
                Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

                //這里的處理方式是簡單的把預覽的一幀圖保存下色查。如果需要做人臉設別或者其他操作,可以拿到這個bitmap進行分析處理
                //我們可以通過找出這張圖片發(fā)現(xiàn)預覽保存的圖片的方向是不對的跟伏,還是Camera的原始方向丢胚,需要旋轉一定角度才可以。
                if(curCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
                    bitmap = rotate(bitmap,90);
                }else {
                    bitmap = mirror(rotate(bitmap,270));
                }
                saveBitmapToFile(bitmap,"oneShot.jpg");
            }
        });


        mCamera.startPreview();

    }

 void saveBitmapToFile(Bitmap bitmap, String fileName) {
        File file = new File(MyApplication.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName);
        if (file.exists()) {
            file.delete();
        }
        try {
            file.createNewFile();
            FileOutputStream fos = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.flush();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

//水平鏡像翻轉
    public Bitmap mirror(Bitmap rawBitmap) {
        Matrix matrix = new Matrix();
        matrix.postScale(-1f, 1f);
        return Bitmap.createBitmap(rawBitmap, 0, 0, rawBitmap.getWidth(), rawBitmap.getHeight(), matrix, true);
    }

    //旋轉
    public Bitmap rotate(Bitmap rawBitmap, float degree) {
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        return Bitmap.createBitmap(rawBitmap, 0, 0, rawBitmap.getWidth(), rawBitmap.getHeight(), matrix, true);
    }
  1. Camera的切換
private void switchCamera() {
        if (mCamera != null) {
            releaseCamera();
            initCamera((curCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) ? Camera.CameraInfo.CAMERA_FACING_BACK : Camera.CameraInfo.CAMERA_FACING_FRONT);
            try {
                mCamera.setPreviewDisplay(mSurfaceHolder);
            } catch (IOException e) {
                e.printStackTrace();
            }
            startPreview();
        }
    }

    private void releaseCamera() {
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

三受扳、遇到的問題 (頁面卡住携龟、黑屏、倒立等)

問題一 :切換攝像頭后畫面卡住

解決:需要先關閉Camera釋放資源勘高,然后重新打開切換后的Camera峡蟋,重新設置PreviewDisplay 然后開始預覽

問題二: 頁面重新打開后(在預覽頁按Home鍵推到后臺,然后再回到前臺)SurfaceView的內容黑屏

解決:通過查看log看到有一個異常信息:“Camera is being used after Camera.release() was called” 原來是在surfaceDestroyed時調用了Camera的release 但是沒有設置為null, 在surfaceCreated的時候是根據(jù)camera是否為空來判斷是否需要重新初始化相满。

問題三:前攝像頭預覽出現(xiàn)倒立并且是鏡像狀態(tài)

public static void setCamerDisplayOrientation(Activity activity, int cameraId, Camera camera) {
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, cameraInfo);
        int result = 0;
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (cameraInfo.orientation) % 360;
        } 
        camera.setDisplayOrientation(result);

解決:

`public static void setCamerDisplayOrientation(Activity activity, int cameraId, Camera camera) {
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, cameraInfo);
        int result = 0;
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (cameraInfo.orientation) % 360;
            result = (360 - result) % 360;
        } 
        camera.setDisplayOrientation(result);


圖片來自[Android: Camera相機開發(fā)詳解(上)]

問題四:很多時候层亿,不僅僅要預覽,在預覽視頻的時候立美,希望能做一些檢測匿又,比如人臉檢測等。這就需要獲得預覽幀視頻建蹄,該如何做吶碌更?

Camera提供了setPreviewCallback、setOneShotPreviewCallback以及setPreviewCallbackWithBuffer三個方法供使用者進行幀回調處理洞慎。比如下面的處理時痛单,通過setOneShotPreviewCallback獲取一幀的bitmap,然后進行保存到文件

mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
Log.i(TAG, "onPreviewFrame: setOneShotPreviewCallback");
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
ByteArrayOutputStream os = new ByteArrayOutputStream(data.length);
if(!yuvImage.compressToJpeg(new Rect(0,0,previewSize.width,previewSize.height),100,os)){
Log.e(TAG, "onPreviewFrame: compressToJpeg error" );
return;
}
byte[] bytes = os.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

            //這里的處理方式是簡單的把預覽的一幀圖保存下劲腿。如果需要做人臉設別或者其他操作旭绒,可以拿到這個bitmap進行分析處理
          FileUtils.saveBitmapToFile(bitmap,"oneShot.jpg");
        }
    });

問題五:發(fā)現(xiàn)保存的圖片和預覽的圖片的方向不一致

解決: 預覽通過Camera的setDisplay Orientation根據(jù)前后攝像頭的需旋轉的角度進行了處理,但是保存為圖片是和預覽時的設置時不相關的焦人,需要單獨處理

222.png

圖片來自[Android: Camera相機開發(fā)詳解(上)]

//我們可以通過找出這張圖片發(fā)現(xiàn)預覽保存的圖片的方向是不對的挥吵,還是Camera的原始方向,需要旋轉一定角度才可以花椭。

                if(curCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
                    bitmap = BitmapUtils.rotate(bitmap,90);
                }else {
                    bitmap = BitmapUtils.mirror(BitmapUtils.rotate(bitmap,270));
                }


public class BitmapUtils {
    //水平鏡像翻轉
    public static Bitmap mirror(Bitmap rawBitmap) {
        Matrix matrix = new Matrix();
        matrix.postScale(-1f, 1f);
        return Bitmap.createBitmap(rawBitmap, 0, 0, rawBitmap.getWidth(), rawBitmap.getHeight(), matrix, true);
    }

    //旋轉
    public static Bitmap rotate(Bitmap rawBitmap, float degree) {
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        return Bitmap.createBitmap(rawBitmap, 0, 0, rawBitmap.getWidth(), rawBitmap.getHeight(), matrix, true);
    }
}

參考資料

《音視頻開發(fā)進階指南》
[Android: Camera相機開發(fā)詳解(上)]
Android camera2 實現(xiàn)相機預覽及獲取預覽幀數(shù)據(jù)流
Activity啟動后View何時開始繪制(onCreate中還是onResume之后忽匈?)

收獲

  1. Camera的Facing、Orientation 矿辽、Size丹允、PreviewCallback等基礎知識的實踐和了解
  2. camera預覽的流程熟悉
  3. 黑屏郭厌、卡頓、倒立雕蔽、鏡像等問題的分析和處理處理

感謝你的閱讀折柠。
下一篇我們來一起學習實踐下MediaExtractor和MediaMuxer 來解析和封裝MP4文件。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末萎羔,一起剝皮案震驚了整個濱河市液走,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贾陷,老刑警劉巖缘眶,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異髓废,居然都是意外死亡巷懈,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門慌洪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來顶燕,“玉大人,你說我怎么就攤上這事冈爹∮抗ィ” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵频伤,是天一觀的道長恳谎。 經常有香客問我,道長憋肖,這世上最難降的妖魔是什么因痛? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮岸更,結果婚禮上鸵膏,老公的妹妹穿的比我還像新娘。我一直安慰自己怎炊,他們只是感情好谭企,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著评肆,像睡著了一般债查。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上糟港,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音院仿,去河邊找鬼秸抚。 笑死速和,一個胖子當著我的面吹牛,可吹牛的內容都是我干的剥汤。 我是一名探鬼主播颠放,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吭敢!你這毒婦竟也來了碰凶?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鹿驼,失蹤者是張志新(化名)和其女友劉穎欲低,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體畜晰,經...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡砾莱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了凄鼻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腊瑟。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖块蚌,靈堂內的尸體忽然破棺而出闰非,到底是詐尸還是另有隱情,我是刑警寧澤峭范,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布财松,位于F島的核電站,受9級特大地震影響虎敦,放射性物質發(fā)生泄漏游岳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一其徙、第九天 我趴在偏房一處隱蔽的房頂上張望胚迫。 院中可真熱鬧,春花似錦唾那、人聲如沸访锻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽期犬。三九已至,卻和暖如春避诽,著一層夾襖步出監(jiān)牢的瞬間龟虎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工沙庐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鲤妥,地道東北人佳吞。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像棉安,于是被迫代替她去往敵國和親底扳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355