Media Module之Camera(四) 拍照 底層分析

下面主要介紹拍照流程的底層實現。


流程圖
流程圖2
狀態機
狀態機2

當指定了Camera的預覽類,并開始預覽之后,就可以通過takePicture()方法進行拍照了。它將以異步的方式從Camera中獲取圖像,具有多個回調類作為參數,并且都可以為null,下面分別介紹這些參數的意義:
? shutter:在按下快門的時候回調,這里可以播放一段聲音。
? raw:從Camera獲取到未經處理的圖像。
? postview:從Camera獲取一個快速預覽的圖片,不是所有設備都支持。
? jpeg:從Camera獲取到一個經過壓縮的jpeg圖片。
  雖然raw、postview、jpeg都是Camera.PictureCallback回調,但是一般我們只需要獲取jpeg,其他傳null即可,Camera.PictureCallback里需要實現一個方法onPictureTaken(byte[] data,Camera camera),data及為圖像數據。值得注意的是,一般taskPicture()方法拍照完成之后,SurfaceView都會停留在拍照的瞬間,需要重新調用startPreview()才會繼續預覽。
  如果直接使用taskPicture()進行拍照的話,Camera是不會進行自動對焦的,這里需要使用Camera.autoFocus()方法進行對焦,它傳遞一個Camera.AutoFocusCallback參數,用于自動對焦完成后回調,一般會在它對焦完成在進行taskPicture()拍照。

4.2.1 java framework

首先拍照的流程直接從Camera.java的takePicture開始分析。

public final void takePicture(ShutterCallback shutter, PictureCallback raw,
        PictureCallback postview, PictureCallback jpeg) {
    android.util.SeempLog.record(65);
    mShutterCallback = shutter;
    mRawImageCallback = raw;
    mPostviewCallback = postview;
    mJpegCallback = jpeg;

    // If callback is not set, do not send me callbacks.
    int msgType = 0;
    if (mShutterCallback != null) {
        msgType |= CAMERA_MSG_SHUTTER;
    }
    if (mRawImageCallback != null) {
        msgType |= CAMERA_MSG_RAW_IMAGE;
    }
    if (mPostviewCallback != null) {
        msgType |= CAMERA_MSG_POSTVIEW_FRAME;
    }
    if (mJpegCallback != null) {
        msgType |= CAMERA_MSG_COMPRESSED_IMAGE;
    }

    native_takePicture(msgType);
    mFaceDetectionRunning = false;
}

可以看出,在方法中對各種回調的值進行了賦值,繼續看底層對調函數的處理。

@Override
public void handleMessage(Message msg) {
    switch(msg.what) {
    //有數據到達通知
    case CAMERA_MSG_SHUTTER:
        if (mShutterCallback != null) {
            mShutterCallback.onShutter();
        }
        return;
    //處理未壓縮照片函數
    case CAMERA_MSG_RAW_IMAGE:
        if (mRawImageCallback != null) {
            mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera);
        }
        return;
    //處理壓縮處理的照片函數
    case CAMERA_MSG_COMPRESSED_IMAGE:
        if (mJpegCallback != null) {
            mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera);
        }
        return;
    ... ...

在應用層注冊回調。
packages\apps\SnapdragonCamera\src\com\android\camera\AndroidCameraManagerImpl.java

@Override
public void takePicture(
        Handler handler,
        CameraShutterCallback shutter,
        CameraPictureCallback raw,
        CameraPictureCallback post,
        CameraPictureCallback jpeg) {
    mCameraHandler.requestTakePicture(
            ShutterCallbackForward.getNewInstance(handler, this, shutter),
            PictureCallbackForward.getNewInstance(handler, this, raw),
            PictureCallbackForward.getNewInstance(handler, this, post),
            PictureCallbackForward.getNewInstance(handler, this, jpeg));
}

在應用層實現回調。

@Override
public void onPictureTaken(final byte [] jpegData, CameraProxy camera) {
... ...
if (mRefocus) {
    final String[] NAMES = { "00.jpg", "01.jpg", "02.jpg", "03.jpg",
        "04.jpg", "DepthMapImage.y", "AllFocusImage.jpg" };
    try {
        FileOutputStream out = mActivity.openFileOutput(NAMES[mReceivedSnapNum - 1],
                Context.MODE_PRIVATE);
        out.write(jpegData, 0, jpegData.length);
        out.close();
    } catch (Exception e) {
    }
}
... ...

這里就是真正存儲數據的地方了,在android系統有四個地方可以存儲共同數據區,
ContentProvider,sharedpreference、file、sqlite這幾種方式,這里利用的是file方式。

4.2.2 JNI

然后調用到JNI層相應方法。

**static void **android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz,
 jint msgType)
 {
     ALOGV("takePicture");
     JNICameraContext* context;
     sp<Camera> camera =
 get_native_camera(env, thiz, &context);
     **if **(camera == 0) **return**;

     /*
      * When CAMERA_MSG_RAW_IMAGE is
 requested, if the raw image callback
      * buffer is available,
 CAMERA_MSG_RAW_IMAGE is enabled to get the
      * notification _and_ the data;
 otherwise, CAMERA_MSG_RAW_IMAGE_NOTIFY
      * is enabled to receive the
 callback notification but no data.
      ***
 **     * Note that
 CAMERA_MSG_RAW_IMAGE_NOTIFY is not exposed to the
      * Java application.
      */
     **if **(msgType &
 CAMERA_MSG_RAW_IMAGE) {
         ALOGV("Enable raw image
 callback buffer");
         **if **(!context->isRawImageCallbackBufferAvailable())
 {
             ALOGV("Enable raw
 image notification, since no callback buffer exists");
             msgType &=
 ~CAMERA_MSG_RAW_IMAGE;
             msgType |= CAMERA_MSG_RAW_IMAGE_NOTIFY;
         }
     }

     **if **(camera->takePicture(msgType)
 != NO_ERROR) {
         jniThrowRuntimeException(env,
 "takePicture failed");
         **return**;
     }
 }

4.2.3 Native層

根據之前分析的binder機制,Camera.cpp -> ICamera.cpp -> CameraClient.cpp(server端)

// take a picture - image is returned in
 callback
 status_t CameraClient::takePicture(**int **msgType) {
     LOG1("takePicture (pid %d):
 0x%x", getCallingPid(), msgType);

     Mutex::Autolock lock(mLock);
     status_t result =
 checkPidAndHardware();
     **if **(result != NO_ERROR) **return
 **result;

     **if **((msgType &
 CAMERA_MSG_RAW_IMAGE) &&
         (msgType &
 CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
        
 ALOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"
                 " cannot be both
 enabled");
         **return **BAD_VALUE;
     }

     // We only accept picture related
 message types
     // and ignore other types of
 messages for takePicture().
     **int **picMsgType = msgType
                         &
 (CAMERA_MSG_SHUTTER |
                           
 CAMERA_MSG_POSTVIEW_FRAME |
                           
 CAMERA_MSG_RAW_IMAGE |
                           
 CAMERA_MSG_RAW_IMAGE_NOTIFY |
                           
 CAMERA_MSG_COMPRESSED_IMAGE);

     enableMsgType(picMsgType);
     mBurstCnt =
 mHardware->getParameters().getInt("num-snaps-per-shutter");
     **if**(mBurstCnt <= 0)
         mBurstCnt = 1;
     LOG1("mBurstCnt = %d",
 mBurstCnt);

     **return **mHardware->takePicture();
 }

此處的takepicture是在CameraHardwareInterface.h定義的方法。

frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.h

status_t takePicture()
 {
     ALOGV("%s(%s)",
 __FUNCTION__, mName.string());
     **if **(mDevice->ops->take_picture)
         **return **mDevice->ops->take_picture(mDevice);
     **return **INVALID_OPERATION;
 }

4.2.4 HAL層

在CameraClient.cpp開始調用到HAL層中進行處理,接下來主要分析在

hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp得具體實現。

**int **QCamera2HardwareInterface::take_picture(**struct
 **camera_device *device)
 {
     ATRACE_CALL();
     **int **ret = NO_ERROR;
     QCamera2HardwareInterface *hw =
         **reinterpret_cast**<QCamera2HardwareInterface
 *>(device->priv);
     ... ...
     /* Prepare snapshot in case LED
 needs to be flashed */
     **if **(hw->mFlashNeeded == **true
 **|| hw->mParameters.isChromaFlashEnabled()) {
         ret = hw->processAPI(QCAMERA_SM_EVT_PREPARE_SNAPSHOT,
 NULL);
         **if **(ret == NO_ERROR) {
             hw->waitAPIResult(QCAMERA_SM_EVT_PREPARE_SNAPSHOT,
 &apiResult);
             ret = apiResult.status;
         }
         hw->mPrepSnapRun = **true**;
     }

     /* Regardless what the result value
 for prepare_snapshot,
      * go ahead with capture anyway.
 Just like the way autofocus
      * is handled in capture case. */

     /* capture */
     ret = hw->processAPI(QCAMERA_SM_EVT_TAKE_PICTURE,
 NULL);
     **if **(ret == NO_ERROR) {
         hw->waitAPIResult(QCAMERA_SM_EVT_TAKE_PICTURE,
 &apiResult);
         ret = apiResult.status;
     }
     ... ...
 }

在此方法中去更新狀態機的狀態。

hardware/qcom/camera/QCamera2/HAL/QCameraStateMachine.cpp

**case **QCAMERA_SM_EVT_PREPARE_SNAPSHOT:
     {
         rc =
 m_parent->prepareHardwareForSnapshot(FALSE);
         **if **(rc == NO_ERROR) {
             // Do not signal API result
 in this case.
             // Need to wait for
 snapshot done in metadta.
             m_state =
 QCAMERA_SM_STATE_PREPARE_SNAPSHOT;
         } **else **{
             // Do not change state in
 this case.
             ALOGE("%s:
 prepareHardwareForSnapshot failed %d",
                 __func__, rc);

             result.status = rc;
             result.request_api = evt;
             result.result_type =
 QCAMERA_API_RESULT_TYPE_DEF;
            
 m_parent->signalAPIResult(&result);
         }
     }
     **break**;

首先調用回QCamera2HWI.cpp的prepareHardwareForSnapshot方法。

int32_t
 QCamera2HardwareInterface::prepareHardwareForSnapshot(int32_t afNeeded)
 {
     ATRACE_CALL();
     CDBG_HIGH("[KPI Perf] %s:
 Prepare hardware such as LED",__func__);
     **return **mCameraHandle->ops->prepare_snapshot(mCameraHandle->camera_handle,
                                               
 afNeeded);
 }

接著調用到mm_camera_interface.c的mm_camera_intf_prepare_snapshot方法。

hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera_interface.c

**static **int32_t
 mm_camera_intf_prepare_snapshot(uint32_t camera_handle,
                                               
 int32_t do_af_flag)
 {
     int32_t rc = -1;
     mm_camera_obj_t * my_obj = NULL;

    
 pthread_mutex_lock(&g_intf_lock);
     my_obj =
 mm_camera_util_get_camera_by_handler(camera_handle);

     **if**(my_obj) {
        
 pthread_mutex_lock(&my_obj->cam_lock);
         pthread_mutex_unlock(&g_intf_lock);
         rc =
 mm_camera_prepare_snapshot(my_obj, do_af_flag);
     } **else **{
        
 pthread_mutex_unlock(&g_intf_lock);
     }
     **return **rc;
 }

接著調用mm_camera.c的mm_camera_prepare_snapshot方法,去與V4L2通信,準備拍照。

hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera.c

int32_t
 mm_camera_prepare_snapshot(mm_camera_obj_t *my_obj,
                                   
 int32_t do_af_flag)
 {
     int32_t rc = -1;
     int32_t value = do_af_flag;
     rc =
 mm_camera_util_s_ctrl(my_obj->ctrl_fd, CAM_PRIV_PREPARE_SNAPSHOT,
 &value);
    
 pthread_mutex_unlock(&my_obj->cam_lock);
     **return **rc;
 }

當底層對拍照準備完成之后,會調用到QCamera2HWICallbacks.cpp里,處理metadata

數據的方法metadata_stream_cb_routine中。

hardware/qcom/camera/QCamera2/HAL/QCamera2HWICallbacks.cpp

**if **(pMetaData->is_prep_snapshot_done_valid)
 {
     qcamera_sm_internal_evt_payload_t
 *payload =
        
 (qcamera_sm_internal_evt_payload_t *)malloc(**sizeof**(qcamera_sm_internal_evt_payload_t));
     **if **(NULL != payload) {
         memset(payload, 0, **sizeof**(qcamera_sm_internal_evt_payload_t));
         payload->evt_type =
 QCAMERA_INTERNAL_EVT_PREP_SNAPSHOT_DONE;
         payload->prep_snapshot_state
 = pMetaData->prep_snapshot_done_state;
         int32_t rc =
 pme->processEvt(QCAMERA_SM_EVT_EVT_INTERNAL, payload);
         **if **(rc != NO_ERROR) {
             ALOGE("%s: processEvt
 prep_snapshot failed", __func__);
             free(payload);
             payload = NULL;
         }
     } **else **{
         ALOGE("%s: No memory for
 prep_snapshot qcamera_sm_internal_evt_payload_t", __func__);
     }
 }

此處給狀態機設置了新的狀態

**case **QCAMERA_INTERNAL_EVT_PREP_SNAPSHOT_DONE:
     CDBG("%s: Received
 QCAMERA_INTERNAL_EVT_PREP_SNAPSHOT_DONE event",
         __func__);
    
 m_parent->processPrepSnapshotDoneEvent(internal_evt->prep_snapshot_state);
     m_state =
 QCAMERA_SM_STATE_PREVIEWING;

     result.status = NO_ERROR;
     result.request_api =
 QCAMERA_SM_EVT_PREPARE_SNAPSHOT;
     result.result_type =
 QCAMERA_API_RESULT_TYPE_DEF;
    
 m_parent->signalAPIResult(&result);
     **break**;

之后返回到QCamera2HWI.cpp類里的take_picture方法,繼續下面的操作。

ret =
 hw->processAPI(QCAMERA_SM_EVT_TAKE_PICTURE, NULL);
 **if **(ret == NO_ERROR) {
    
 hw->waitAPIResult(QCAMERA_SM_EVT_TAKE_PICTURE, &apiResult);
     ret = apiResult.status;
 }
此處改變了狀態機的狀態,QCAMERA_SM_EVT_TAKE_PICTURE

**case **QCAMERA_SM_EVT_TAKE_PICTURE:
    {
        **if **(
 m_parent->mParameters.getRecordingHintValue() == **true**) {
             m_parent->stopPreview();
            
 m_parent->mParameters.updateRecordingHintValue(FALSE);
             // start preview again
             rc = m_parent->preparePreview();
             **if **(rc == NO_ERROR)
 {
                 rc =
 m_parent->startPreview();
                 **if **(rc !=
 NO_ERROR) {
                    
 m_parent->unpreparePreview();
                 }
             }
        }
        **if **(m_parent->isZSLMode()
 || m_parent->isLongshotEnabled()) {
            m_state =
 QCAMERA_SM_STATE_PREVIEW_PIC_TAKING;
            rc =
 m_parent->takePicture();
            **if **(rc != NO_ERROR) {
                // move state to
 previewing state
                m_state = QCAMERA_SM_STATE_PREVIEWING;
            }
        } **else **{
            m_state =
 QCAMERA_SM_STATE_PIC_TAKING;
            rc =
 m_parent->takePicture();
            **if **(rc != NO_ERROR) {
                // move state to preview
 stopped state
                m_state =
 QCAMERA_SM_STATE_PREVIEW_STOPPED;
            }
        }

        result.status = rc;
        result.request_api = evt;
        result.result_type =
 QCAMERA_API_RESULT_TYPE_DEF;
       
 m_parent->signalAPIResult(&result);
     }
     **break**;

此處又返回到QCamera2HWI.cpp類里的takePicture()方法中。

**int **QCamera2HardwareInterface::takePicture()
 {
 **    ... ...**
             } **else **{
                 // normal capture case
                 // need to stop preview
 channel
                
 stopChannel(QCAMERA_CH_TYPE_PREVIEW);
                
 delChannel(QCAMERA_CH_TYPE_PREVIEW);
                 rc =
 addCaptureChannel();
             }
 **    ... ...**
 }

首先先停止并刪除了preview的channel,然后調用addCaptureChannel()方法。

int32_t
 QCamera2HardwareInterface::addCaptureChannel()
 {

... ...

rc = pChannel->init(&attr,
                    
 capture_channel_cb_routine,
                     **this**);

... ...

rc = addStreamToChannel(pChannel,
 CAM_STREAM_TYPE_METADATA,
                        
 metadata_stream_cb_routine, **this**);

... ...

rc = addStreamToChannel(pChannel,
 CAM_STREAM_TYPE_POSTVIEW,
                         NULL, **this**);

... ...

rc = addStreamToChannel(pChannel,
 CAM_STREAM_TYPE_SNAPSHOT,
                         NULL, **this**);

... ...

可以看出,在此方法中創建并初始化了channel,并且添加mediadata,postview,snapshot數據流到channel中。

返回到hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp的takePicture()方法中,繼續調用到開啟capture的channel。

rc = 
 m_channels[QCAMERA_CH_TYPE_CAPTURE]->start();

channel的start方法在之前的preview流程中具體介紹過,在此不做分析。

之后又調用了QCameraChannel.cpp的startAdvancedCapture方法,然后是mm-camera-interface.c的process_advanced_capture方法,然后是mm-camera.c的mm_camera_channel_advanced_capture方法,這一系列方法設置了當前管道是拍照模式。

4.2.5 jpeg數據流

通過mm-jpeg-interface.c處理數據流,并且生成jpeg文件,然后在QCamera2HWI.cpp中處理從mm-jpeg-interface.c發出的jpeg相關事件。

**void **QCamera2HardwareInterface::jpegEvtHandle(jpeg_job_status_t
 status,
                                              
 uint32_t /*client_hdl*/,
                                              
 uint32_t jobId,
                                              
 mm_jpeg_output_t *p_output,
                                              
 **void ***userdata)
 {
     QCamera2HardwareInterface *obj =
 (QCamera2HardwareInterface *)userdata;
     **if **(obj) {
         qcamera_jpeg_evt_payload_t
 *payload =
             (qcamera_jpeg_evt_payload_t
 *)malloc(**sizeof**(qcamera_jpeg_evt_payload_t));
         **if **(NULL != payload) {
             memset(payload, 0, **sizeof**(qcamera_jpeg_evt_payload_t));
             payload->status =
 status;
             payload->jobId = jobId;
             **if **(p_output !=
 NULL) {
                 payload->out_data =
 *p_output;
             }
            
 obj->processMTFDumps(payload);
            
 obj->processEvt(QCAMERA_SM_EVT_JPEG_EVT_NOTIFY, payload);
         }
     } **else **{
         ALOGE("%s: NULL
 user_data", __func__);
     }
 }

此處更改了狀態機的狀態。


**case **QCAMERA_SM_EVT_JPEG_EVT_NOTIFY:
     {
         qcamera_jpeg_evt_payload_t
 *jpeg_job =
             (qcamera_jpeg_evt_payload_t
 *)payload;
         rc =
 m_parent->processJpegNotify(jpeg_job);
     }
     **break**;

然后調用回QCamera2HWI.cpp的processJpegNotify方法

int32_t
 QCamera2HardwareInterface::processJpegNotify(qcamera_jpeg_evt_payload_t
 *jpeg_evt)
 {
     **return **m_postprocessor.processJpegEvt(jpeg_evt);
 }

此處調用的是QCameraPostProc.cpp的processJpegEvt方法。

hardware/qcom/camera/QCamera2/HAL/QCameraPostProc.cpp

int32_t
 QCameraPostProcessor::processJpegEvt(qcamera_jpeg_evt_payload_t *evt)
 {
     ... ...
         rc =
 sendDataNotify(CAMERA_MSG_COMPRESSED_IMAGE,
                             jpeg_mem,
                             0,
                             NULL,
                             &release_data);
     ... ...
     return rc;
 }

接著調用sendDataNotify方法。

int32_t
 QCameraPostProcessor::sendDataNotify(int32_t msg_type,
                                             
 camera_memory_t *data,
                                             
 uint8_t index,
                                             
 camera_frame_metadata_t *metadata,
                                             
 qcamera_release_data_t *release_data)
 {
     qcamera_data_argm_t *data_cb =
 (qcamera_data_argm_t *)malloc(**sizeof**(qcamera_data_argm_t));
     **if **(NULL == data_cb) {
         ALOGE("%s: no mem for
 acamera_data_argm_t", __func__);
         **return **NO_MEMORY;
     }
     memset(data_cb, 0, **sizeof**(qcamera_data_argm_t));
     data_cb->msg_type = msg_type;
     data_cb->data = data;
     data_cb->index = index;
     data_cb->metadata = metadata;
     **if **(release_data != NULL) {
         data_cb->release_data =
 *release_data;
     }

     qcamera_callback_argm_t cbArg;
     memset(&cbArg, 0, **sizeof**(qcamera_callback_argm_t));
     cbArg.cb_type =
 QCAMERA_DATA_SNAPSHOT_CALLBACK;
     cbArg.msg_type = msg_type;
     cbArg.data = data;
     cbArg.metadata = metadata;
     cbArg.user_data = data_cb;
     cbArg.cookie = **this**;
     cbArg.release_cb = releaseNotifyData;
     **int **rc =
 m_parent->m_cbNotifier.notifyCallback(cbArg);
     **if **( NO_ERROR != rc ) {
         ALOGE("%s: Error enqueuing
 jpeg data into notify queue", __func__);
         releaseNotifyData(data_cb, **this**,
 UNKNOWN_ERROR);
         **return **UNKNOWN_ERROR;
    
 }

     **return **rc;
 }

可以看出此處在給回調的對象裝填數據,并且發出通知notifyCallback回調。并且,類型為CAMERA_MSG_COMPRESSED_IMAGE。

之后的流程與preview的流程相似,將數據向上層拋送,通過JNI返回到java層的回調函數中。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容