MediaCodec API,完成視頻 H.264 的硬編奸例、硬解

一彬犯、編碼基礎概念

1.1 為什么要進行視頻編碼

視頻是由一幀幀圖像組成,就如常見的gif圖片查吊,如果打開一張gif圖片谐区,可以發(fā)現里面是由很多張圖片組成。一般視頻為了不讓觀眾感覺到卡頓逻卖,一秒鐘至少需要24幀畫面(一般是30幀)宋列,假如該視頻是一個1280x720分辨率的視頻,那么不經過編碼一秒鐘的大衅酪病:
結果:1280x720x4x24/(1024*1024)≈84.375M

所以不經過編碼的視頻根本沒法保存炼杖,更不用說傳輸了。

1.2 視頻壓縮編碼標準

視頻中存在很多冗余信息盗迟,比如圖像相鄰像素之間有較強的相關性坤邪,視頻序列的相鄰圖像之間內容相似,人的視覺系統(tǒng)對某些細節(jié)不敏感等罚缕,對這部分冗余信息進行處理的過程就是視頻編碼艇纺。

H.26X系列(由ITU[國際電傳視訊聯盟]主導)
H.261:主要在老的視頻會議和視頻電話產品中使用
H.263:主要用在視頻會議、視頻電話和網絡視頻上
H.264:H.264/MPEG-4第十部分邮弹,或稱AVC(Advanced Video Coding黔衡,高級視頻編碼),是一種視頻壓縮標準腌乡,一種被廣泛使用的高精度視頻的錄制盟劫、壓縮和發(fā)布格式。
H.265:高效率視頻編碼(High Efficiency Video Coding与纽,簡稱HEVC)是一種視頻壓縮標準侣签,H.264/MPEG-4 AVC的繼任者塘装。可支持4K分辨率甚至到超高畫質電視硝岗,最高分辨率可達到8192×4320(8K分辨率)氢哮,這是目前發(fā)展的趨勢,尚未有大眾化編碼軟件出現

MPEG系列(由ISO[國際標準組織機構]下屬的MPEG[運動圖象專家組]開發(fā))
MPEG-1第二部分:MPEG-1第二部分主要使用在VCD上型檀,有些在線視頻也使用這種格式
MPEG-2第二部分(MPEG-2第二部分等同于H.262,使用在DVD听盖、SVCD和大多數數字視頻廣播系統(tǒng)中
MPEG-4第二部分(MPEG-4第二部分標準可以使用在網絡傳輸胀溺、廣播和媒體存儲上

1.3 編碼流程

在進行當前信號編碼時,編碼器首先會產生對當前信號做預測的信號皆看,稱作預測信號(predicted signal)

預測的方式

時間上的預測(interprediction)仓坞,亦即使用先前幀的信號做預測
空間上的預測 (intra prediction),亦即使用同一張幀之中相鄰像素的信號做預測
得到預測信號后腰吟,編碼器會將當前信號與預測信號相減得到殘余信號(residual signal)无埃,并只對殘余信號進行編碼,如此一來,可以去除一部份時間上或是空間上的冗余信息毛雇。

編碼器并不會直接對殘余信號進行編碼嫉称,而是先將殘余信號經過變換(通常為離散余弦變換)然后量化以進一步去除空間上和感知上的冗余信息。量化后得到的量化系數會再透過熵編碼灵疮,去除統(tǒng)計上的冗余信息织阅。

二、H.264編碼詳解(AVC)

2.1 H.264是新一代的編碼標準震捣,以高壓縮高質量和支持多種網絡的流媒體傳輸著稱荔棉。

相關理解:

在相鄰幾幅圖像畫面中,一般有差別的像素只有10%以內的點,亮度差值變化不超過2%蒿赢,而色度差值的變化只有1%以內润樱,所以對于一段變化不大圖像畫面,我們可以先編碼出一個完整的圖像幀A羡棵,隨后的B幀就不編碼全部圖像壹若,只寫入與A幀的差別,這樣B幀的大小就只有完整幀的1/10或更小晾腔,B幀之后的C幀如果變化不大舌稀,我們可以繼續(xù)以參考B的方式編碼C幀,這樣循環(huán)下去灼擂。
這段圖像我們稱為一個序列壁查,序列就是有相同特點的一段數據,當某個圖像與之前的圖像變化很大剔应,無法參考前面的幀來生成睡腿,那我們就結束上一個序列语御,開始下一段序列,也就是對這個圖像生成一個完整幀A1席怪,隨后的圖像就參考A1生成应闯,只寫入與A1的差別內容。

主要目標:

高的視頻壓縮比挂捻;
良好的網絡親和性碉纺;

2.2 H.264三種幀

在H.264中定義了三種幀:
????I幀:完整編碼的幀叫I幀
????P幀:參考之前的I幀生成的只包含差異部分編碼的幀叫P幀
????B幀:參考前后的幀編碼的幀叫B幀

H264采用的核心算法是幀內壓縮和幀間壓縮:
????幀內壓縮是生成I幀的算法
????幀間壓縮是生成B幀和P幀的算法

壓縮方法:
????分組:把幾幀圖像分為一組(GOP,也就是一個序列),為防止運動變化,幀數不宜取多
????定義幀:將每組內各幀圖像定義為三種類型,即I幀刻撒、B幀和P幀;
????預測幀:以I幀做為基礎幀,以I幀預測P幀,再由I幀和P幀預測B幀;
????數據傳輸:最后將I幀數據與預測的差值信息進行存儲和傳輸骨田。

GOP序列:
????在H264中圖像以序列為單位進行組織,一個序列是一段圖像編碼后的數據流声怔。
????一個序列的第一個圖像叫做 IDR 圖像(立即刷新圖像)态贤,IDR 圖像都是 I 幀圖像

2.3 編碼YUV為H.26X編碼視頻格式

YUV數據

YUV通過Y,U醋火,V三個分量表示顏色空間悠汽,Y表示亮度,UV表示色度芥驳。RGB顏色空間每個像素點都有獨立的RGB三個顏色分量值柿冲。YUV卻不同:
YUV根據UV采樣數目的不同,分為YUV444晚树,YUV422姻采,YUV420等。

YUV420

表示每個像素點有一個獨立的亮度表示爵憎,即Y慨亲;色度UV分量由每四個像素點共享一個。例如一個4X4的圖片宝鼓,在YUV420格式下刑棵,有16個Y,UV各四個愚铡。
YUV420根據UV色度的存儲順序不同蛉签,又分為不同的格式。它分為兩個YUV420P和YUV420SP兩個大類沥寥,YUV420P的UV順序存儲碍舍,YUV420SP的UV交錯存儲。以4X4的圖片格式為例部分格式如下:


YUV420

三邑雅、H.264分層設計

3.1 H264算法在概念上分為兩層:

1片橡、VCL:(Video Coding Layer)視頻編碼層,負責高效的內容表示淮野。
2捧书、NAL:(Network Abstraction Layer)網絡提取層吹泡,負責以網絡所要求的恰當的方式對數據進行打包和傳送。

VCL數據即被壓縮編碼后的視頻數據序列经瓷。在VCL數據要封裝到NAL單元中之后爆哑,才可以用來傳輸或存儲。

H.264 的編碼視頻序列包括一系列的NAL 單元舆吮,每個NAL 單元包含一個RBSP揭朝。一個原始的H.264 NALU 單元常由 [StartCode] [NALU Header] [NALU Payload] 三部分組成,其中 Start Code 用于標示這是一個NALU 單元的開始色冀,必須是"00 00 00 01" 或"00 00 01"萝勤。


NAL單元序列

其中RBPS有分為幾種類型:


典型的RBSP單元

3.2 NAL頭

占一個字節(jié)(8bit),由三部分組成forbidden_bit(1bit)呐伞,nal_reference_bit(2bits)(優(yōu)先級),nal_unit_type(5bits)(類型)慎式。

3.2.1 forbidden_bit

禁止位伶氢,H264文檔規(guī)定,這個值應該為0瘪吏,當它不為0時癣防,表示網絡傳輸過程中,當前NALU中可能存在錯誤掌眠,解碼器可以考慮不對這個NALU進行解碼蕾盯。

3.2.2 nal_reference_bit

當前NAL的優(yōu)先級,值越大蓝丙,該NAL越重要级遭。

3.2.3 nal_unit_type

它表示NALU Header后面的RBSP的數據結構的類型。下圖為nal_unit_type所有可能的取值渺尘,和對應的語義:


nal_unit_type語義

可以看到挫鸽,nal_unit_type的值為1-5時,表示RBSP里面包含的數據為條帶(片/Slice)數據鸥跟,所以值為1-5的NALU統(tǒng)稱為VCL(視像編碼層)單元丢郊,其他的NALU則稱為非VCL NAL單元。

當nal_unit_type為7時医咨,代表當前NALU為序列參數集枫匾,為8時為圖像參數集。這也是我們打開.h264文件后拟淮,遇到的前兩個NALU干茉,它們位于碼流的最前面。

而且當nal_unit_type為14-31時惩歉,我們可以不用理睬等脂,目前幾乎用不到俏蛮。

PPS(Picture Parameter Sets):圖像參數集
SPS(Sequence Parameter Set):序列參數集

接下來說說SPS、PPS和SEI上遥。

SPS和PPS是用來初始化解碼器的搏屑,沒這些數據,視頻數據是無法解析出來的粉楚。另外如果我們分析單獨的H264文件辣恋,可以發(fā)現有的文件每個IDR幀前面都有PPS和SPS,有的只是開頭才有模软。針對SPS和PPS伟骨,一般來說:
1)、如果是在直播的話燃异,每個IDR幀前面都應該加上SPS和PPS携狭,因為有的觀眾會中途進來觀看。
2)回俐、如果是本地穩(wěn)定文件逛腿,可以在開頭加上SPS和PPS,或者都加上仅颇,這個根據具體需要來单默。

另外說明下SEI,有的H264文件有SEI忘瓦,有的則沒有搁廓,這說明SEI對文件的播放并無太大影響。
SEI(Supplemental Enhancement Information):輔助增強信息耕皮。這里面可以存放一些影片簡介境蜕,版權信息或者作者自己添加的一些信息。

四明场、實例

4.1 MediaCodec解碼本地視頻 H.264文件汽摹,然后在SurfaceView上預覽

4.1.1 從本地H264文件中獲取文件流

    public void getFileInputStream() {
        try {
            File file = new File(H264_FILE);
            mInputStream = new DataInputStream(new FileInputStream(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            try {
                mInputStream.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

4.1.2 初始化解碼器

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    private void initMediaCodec() {
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                try {
                    //創(chuàng)建編碼器
                    mCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //初始化編碼器
                final MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC,holder.getSurfaceFrame().width(),holder.getSurfaceFrame().height());
                /*h264常見的幀頭數據為:
                00 00 00 01 67    (SPS)
                00 00 00 01 68    (PPS)
                00 00 00 01 65    (IDR幀)
                00 00 00 01 61    (P幀)*/

                //獲取H264文件中的pps和sps數據
                if (UseSPSandPPS) {
                    byte[] header_sps = {0, 0, 0, 1, 67, 66, 0, 42, (byte) 149, (byte) 168, 30, 0, (byte) 137, (byte) 249, 102, (byte) 224, 32, 32, 32, 64};
                    byte[] header_pps = {0, 0, 0, 1, 68, (byte) 206, 60, (byte) 128, 0, 0, 0, 1, 6, (byte) 229, 1, (byte) 151, (byte) 128};
                    mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps));
                    mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
                }

                //設置幀率
                mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE,40);
                mCodec.configure(mediaFormat,holder.getSurface(),null,0);

                startDecodingThread();
            }

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

            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {

            }
        });
    }

4.1.3 開啟新的線程進行解碼

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    private class DecodeThread implements Runnable{

        @Override
        public void run() {
            //循環(huán)解碼
            decodeLoop();
        }


        private void  decodeLoop(){
            //獲取一組輸入緩存區(qū)
            ByteBuffer[] inputBuffers = mCodec.getInputBuffers();
            //解碼后的數據,包含每一個buffer的元數據信息
            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            long startMs = System.currentTimeMillis();
            long timeoutUs = 10000;
            //用于檢測文件頭
            byte[] maker0 = new byte[]{0,0,0,1};

            byte[] dummyFrame = new byte[]{0x00,0x00,0x01,0x20};
            byte[] streamBuffer = null;

            try {
                //返回可用的字節(jié)數組
                streamBuffer = getBytes(mInputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
            int bytes_cnt = 0;
            while (mStopFlag == false){
                //得到可用字節(jié)數組長度
                bytes_cnt = streamBuffer.length;

                if (bytes_cnt == 0){
                    streamBuffer = dummyFrame;
                }
                int startIndex = 0;
                //定義記錄剩余字節(jié)的變量
                int remaining = bytes_cnt;
                while (true) {
                    //當剩余的字節(jié)=0或者開始的讀取的字節(jié)下標大于可用的字節(jié)數時  不在繼續(xù)讀取
                    if (remaining == 0 || startIndex >= remaining) {
                        break;
                    }
                    //尋找?guī)^部
                    int nextFrameStart = KMPMatch(maker0,streamBuffer,startIndex + 2,remaining);
                    //找不到頭部返回-1
                    if (nextFrameStart == -1) {
                        nextFrameStart = remaining;
                    }
                    //得到可用的緩存區(qū)
                    int inputIndex = mCodec.dequeueInputBuffer(timeoutUs);
                    //有可用緩存區(qū)
                    if (inputIndex >= 0) {
                        ByteBuffer byteBuffer = inputBuffers[inputIndex];
                        byteBuffer.clear();
                        //將可用的字節(jié)數組苦锨,傳入緩沖區(qū)
                        byteBuffer.put(streamBuffer, startIndex, nextFrameStart - startIndex);
                        //把數據傳遞給解碼器
                        mCodec.queueInputBuffer(inputIndex, 0, nextFrameStart - startIndex, 0, 0);
                        //指定下一幀的位置
                        startIndex = nextFrameStart;
                    } else {
                        continue;
                    }

                    int outputIndex = mCodec.dequeueOutputBuffer(info,timeoutUs);
                    if (outputIndex >= 0) {
                        //幀控制是不在這種情況下工作逼泣,因為沒有PTS H264是可用的
                        while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        boolean doRender = (info.size != 0);
                        //對outputbuffer的處理完后,調用這個函數把buffer重新返回給codec類舟舒。
                        mCodec.releaseOutputBuffer(outputIndex, doRender);
                    } else {

                    }
                }
                mStopFlag = true;
            }
        }
    }

源碼地址:https://github.com/Xiaoben336/MediaCodecDecodeH264Demo

4.2 收集Camera數據拉庶,并轉碼為H264存儲到文件

4.2.1 實現方式

視頻采集用Camera 2采集YV12數據編碼為H264視頻文件并保存。
1秃励、我們要實現的功能位通過Camera采集到每幀YUV原始數據
2氏仗、錄制視頻后,編碼YUV原始數據為H.264視頻格式
3、保存H.264編碼格式的視頻文件

在使用Camera的時候皆尔,設置預覽的數據格式為NV21
在使用Camera2的時候呐舔,無法支持預覽數據為NV21,我設置的預覽數據格式YV12

4.2.2 編碼實現

4.2.2.1 初始化MediaCodec

public AvcEncoder(int width, int height, int framerate, File outFile,boolean isCamera) {
        this.mIsCamera = isCamera;
        mWidth = width;
        mHeight = height;
        mFrameRate = framerate;
        mOutFile = outFile;

        MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);//YUV420SP
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, width * height * 5);//*5表示較高的比特率慷蠕,也可以是1珊拼、3
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
        try {
            mMediaCodec = MediaCodec.createEncoderByType("video/avc");
        } catch (IOException e) {
            e.printStackTrace();
        }
        mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        mMediaCodec.start();
        createfile();//創(chuàng)建H264文件的保存位置
    }

4.2.2.2 開始編碼

public boolean isRunning = false;
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void startEncoderThread(){
        Thread encoderThread = new Thread(new Runnable() {
            @Override
            public void run() {
                isRunning = true;
                byte[] input = null;
                long pts = 0;
                long generateIndex = 0;

                while (isRunning) {
                    if (mYuvQueue.size() > 0) {
                        input = mYuvQueue.poll();
                        if (mIsCamera) {//Camera  NV21
                            //NV12數據所需空間為如下,所以建立如下緩沖區(qū)
                            //y=W*h;u=W*H/4;v=W*H/4,so total add is W*H*3/2 (1 + 1/4 + 1/4 = 3/2)
                            byte[] yuv420sp = new byte[mWidth * mHeight *3 /2];
                            NV21ToNV12(input, yuv420sp, mWidth, mHeight);
                            input = yuv420sp;
                        } else {//Camera 2
                            byte[] yuv420sp = new byte[mWidth * mHeight *3 /2];
                            YV12toNV12(input, yuv420sp, mWidth, mHeight);
                            input = yuv420sp;
                        }
                    }

                    if (input != null) {
                        int inputBufferIndex = mMediaCodec.dequeueInputBuffer(-1);
                        if (inputBufferIndex >= 0) {
                            pts = computePresentationTime(generateIndex);
                            ByteBuffer inputBuffer = mMediaCodec.getInputBuffer(inputBufferIndex);
                            inputBuffer.clear();
                            inputBuffer.put(input);
                            mMediaCodec.queueInputBuffer(inputBufferIndex,0,input.length,pts,0);
                            generateIndex += 1;
                        }

                        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                        int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo,TIMEOUT_USEC);
                        while (outputBufferIndex >= 0) {
                            ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex);
                            byte[] outData = new byte[bufferInfo.size];
                            outputBuffer.get(outData);
                            if (bufferInfo.flags == 2) {
                                mConfigByte = new byte[bufferInfo.size];
                                mConfigByte = outData;
                            } else if (bufferInfo.flags == 1) {
                                byte[] keyframe = new byte[bufferInfo.size + mConfigByte.length];
                                System.arraycopy(mConfigByte, 0, keyframe, 0, mConfigByte.length);
                                System.arraycopy(outData, 0, keyframe, mConfigByte.length, outData.length);
                                try {
                                    outputStream.write(keyframe, 0, keyframe.length);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            } else {
                                try {
                                    outputStream.write(outData, 0, outData.length);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }

                            mMediaCodec.releaseOutputBuffer(outputBufferIndex,false);
                            outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo,TIMEOUT_USEC);
                        }
                    } else {
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
        encoderThread.start();
    }

需要把Camera的NV21的預覽數據和Camera2的YV12預覽數據都轉換成了NV12格式的數據流炕。

H.264編碼必須要用NV12澎现,所以我們拿到預覽數據要做格式轉換 .

4.2.3 獲取預覽數據

開啟攝像頭后,獲取預覽數據每辟,參考之前的實現了Camera有回掉很簡單(前面有寫Camera 2的使用)剑辫,Camera2通過ImageRender來獲取。

private static final int STATE_PREVIEW = 0;
    private static final int STATE_RECORD = 1;
    private int mState = STATE_PREVIEW;
    private AvcEncoder mAvcEncoder;
    private int mFrameRate = 30;

    private void setupImageReader() {
        //2代表ImageReader中最多可以獲取兩幀圖像流
        mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(),mPreviewSize.getHeight(), ImageFormat.YV12,1);
        mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Log.e(TAG, "onImageAvailable: "+Thread.currentThread().getName() );
                //這里一定要調用reader.acquireNextImage()和img.close方法否則不會一直回掉了
                Image img = reader.acquireNextImage();
                switch (mState){
                    case STATE_PREVIEW:
                        Log.e(TAG, "mState: STATE_PREVIEW");
                        if (mAvcEncoder != null) {
                            mAvcEncoder.stopThread();
                            mAvcEncoder = null;
                            Toast.makeText(mContext,"停止錄制視頻成功",Toast.LENGTH_SHORT).show();
                        }
                        break;
                    case STATE_RECORD:
                        Log.e(TAG, "mState: STATE_RECORD");
                        Image.Plane[] planes = img.getPlanes();
                        byte[] dataYUV = null;
                        if (planes.length >= 3) {
                            ByteBuffer bufferY = planes[0].getBuffer();
                            ByteBuffer bufferU = planes[1].getBuffer();
                            ByteBuffer bufferV = planes[2].getBuffer();
                            int lengthY = bufferY.remaining();
                            int lengthU = bufferU.remaining();
                            int lengthV = bufferV.remaining();
                            dataYUV = new byte[lengthY + lengthU + lengthV];
                            bufferY.get(dataYUV, 0, lengthY);
                            bufferU.get(dataYUV, lengthY, lengthU);
                            bufferV.get(dataYUV, lengthY + lengthU, lengthV);
                        }

                        if (mAvcEncoder == null) {
                            mAvcEncoder = new AvcEncoder(mPreviewSize.getWidth(),
                                    mPreviewSize.getHeight(), mFrameRate,
                                    getOutputMediaFile(MEDIA_TYPE_VIDEO), false);
                            mAvcEncoder.startEncoderThread();
                            Toast.makeText(mContext, "開始錄制視頻成功", Toast.LENGTH_SHORT).show();
                        }
                        mAvcEncoder.putYUVData(dataYUV);
                        break;
                        default:
                            break;
                }
                img.close();
            }
        },mBackgroundHandler);
    }

源碼地址:https://github.com/Xiaoben336/MediaCodecDecodeH264Demo

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末渠欺,一起剝皮案震驚了整個濱河市妹蔽,隨后出現的幾起案子,更是在濱河造成了極大的恐慌挠将,老刑警劉巖讹开,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異捐名,居然都是意外死亡,警方通過查閱死者的電腦和手機闹击,發(fā)現死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門镶蹋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人赏半,你說我怎么就攤上這事贺归。” “怎么了断箫?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵拂酣,是天一觀的道長。 經常有香客問我仲义,道長婶熬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任埃撵,我火速辦了婚禮赵颅,結果婚禮上,老公的妹妹穿的比我還像新娘暂刘。我一直安慰自己饺谬,他們只是感情好,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布谣拣。 她就那樣靜靜地躺著募寨,像睡著了一般族展。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拔鹰,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天仪缸,我揣著相機與錄音,去河邊找鬼格郁。 笑死腹殿,一個胖子當著我的面吹牛,可吹牛的內容都是我干的例书。 我是一名探鬼主播锣尉,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼决采!你這毒婦竟也來了自沧?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤树瞭,失蹤者是張志新(化名)和其女友劉穎拇厢,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體晒喷,經...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡孝偎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了凉敲。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衣盾。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖爷抓,靈堂內的尸體忽然破棺而出势决,到底是詐尸還是另有隱情,我是刑警寧澤蓝撇,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布果复,位于F島的核電站,受9級特大地震影響渤昌,放射性物質發(fā)生泄漏虽抄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一独柑、第九天 我趴在偏房一處隱蔽的房頂上張望极颓。 院中可真熱鬧,春花似錦群嗤、人聲如沸菠隆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骇径。三九已至躯肌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間破衔,已是汗流浹背清女。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晰筛,地道東北人嫡丙。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像读第,于是被迫代替她去往敵國和親曙博。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內容

  • 視頻壓縮編碼的目標1)保證壓縮比例2)保證恢復的質量3)易實現怜瞒,低成本父泳,可靠性 壓縮的出發(fā)點(可行性)1)時間相關...
    rogerwu1228閱讀 4,160評論 0 11
  • 視頻格式封裝——H264 轉載自 http://blog.csdn.net/yangzhongxuan/artic...
    microchip閱讀 2,372評論 0 1
  • 每日一圖。 圖(單反)/少帥 一曲江水向東吴汪, 一見傾心惠窄, 幾度夕陽余情, 幾許來生漾橙? 文/少帥
    J少帥閱讀 206評論 0 0
  • 在我生命的前十八年里杆融,我從未如此想念過這個小鎮(zhèn)。這個南方小鎮(zhèn)霜运,沿河而盛擒贸,傍山而安,一年四季都是微風繞指觉渴。 我曾在穿...
    陳先生的貓閱讀 536評論 0 0
  • 沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦沒事啦...
    洛文添閱讀 202評論 0 0