Camera 實現(xiàn)錄像

VideoModule.java

@Override
public void onCameraAvailable(CameraProxy cameraProxy) {
    ...
    startPreview(true);
    ...
}

startPreview() 與 PhotoModule 基本一致壹蔓,不做詳解


開始錄像

private void startVideoRecording() {
    //更新 UI轻庆,判斷存儲是否低于設(shè)定的閾值
    ...

    // 初始化 MediaRecorder
    initializeRecorder();
    ...
                    
    try {
        // 調(diào)用 requestAudioFocus 獲取 STREAM_MUSIC 焦點粉楚,暫停正在播放的音頻
        // 調(diào)用 setRingerMode 設(shè)置情景模式為 RINGER_MODE_SILENT错沽,保證錄像期間沒有聲音和震動提示
        ...
        // 啟動錄像
        mMediaRecorder.start(); 
    } catch (IllegalStateException exception) {
        // 實現(xiàn)同 catch (RuntimeException e){}
        return;
    } catch (RuntimeException e) {
        // Could not start media recorder(start failed)
        ...
        // releasing media recorder
        if (mMediaRecorder != null) {
            mMediaRecorder.reset();
            mMediaRecorder.release();
            mMediaRecorder = null;
        }
        // 調(diào)用 setRingerMode 恢復(fù)原有情景模式
        if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
            mAudioManager.setRingerMode(mOriginalRingerMode);
        }
        // If start fails, frameworks will not lock the camera for us.
        mCameraDevice.lock();
        return;
    }
    ...
}


    private MediaRecorder mMediaRecorder;
    private static Class<?> mediaRecoderExClazz = null;
    private static Class<?> mediaRecoderClazz = null;

    private static boolean OLD_SDK_VERSION = 
            Build.VERSION.SDK_INT <= Build.VERSION_CODES.M ? true : false;
    static {
        try {
            mediaRecoderClazz = Class.forName("android.media.MediaRecorder");
            mediaRecoderExClazz = Class.forName("android.media.MediaRecorderEx");
        } catch (Exception e) {
            // get class of MediaRecorder or MediaRecorderEx error
        }

    }
    // Prepares media recorder.
    private void initializeRecorder() {
        ...
        // 初始化 MediaRecorder 對象
        try {
            if (OLD_SDK_VERSION) {
                mMediaRecorder = mediaRecoderClazz.newInstance();
            } else {
                mMediaRecorder = mediaRecoderExClazz.newInstance();
            }
            return mMediaRecorder;
        } catch (Exception e) {
            return null;
        }

        // *必要* 解鎖 Camera捎谨,使得 MediaRecorder 進程能訪問 Camera 硬件
        mCameraDevice.unlock();
        
        // We rely here on the fact that the unlock call above is synchronous
        // and blocks until it occurs in the handler thread. Thereby ensuring
        // that we are up to date with handler requests, and if this proxy had
        // ever been released by a prior command, it would be null.
        Camera camera = mCameraDevice.getCamera();
        // If the camera device is null, the camera proxy is stale and recording
        // should be ignored.
        if (camera == null) {
            Log.w(TAG, "null camera within proxy, not recording");
            return;
        }

        // *必要* 設(shè)置 Camera 用于錄像
        mMediaRecorder.setCamera(camera);

        ...

        Method method;
        try {
            if (mMediaRecorder != null) {
                if (OLD_SDK_VERSION) {
                    method = mediaRecoderClazz.getMethod(
                            "setParam64BitFileOffset", boolean.class);
                    method.invoke(mMediaRecorder, use64BitFlag);
                } else {
                    method = mediaRecoderExClazz.getMethod(
                            "setParam64BitFileOffset", boolean.class);
                    method.invoke(mMediaRecorder, use64BitFlag);
                }
            }
        } catch (Exception e) {
            // setParam64BitFileOffset error
        }

        // *必要* 設(shè)置錄像音頻來源, 使用麥克風(fēng) MediaRecorder.AudioSource.CAMCORDER 作為音頻來源
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
       
        // *必要* 設(shè)置錄像視頻來源, 使用Camera MediaRecorder.VideoSource.CAMERA 作為視頻來源
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

        mMediaRecorder.setProfile(mProfile);
        
        // 設(shè)置視頻尺寸大小芦疏,在 setVideoSource() 和 setOutFormat() 之后 prepare() 之前調(diào)用
        mMediaRecorder.setVideoSize(mProfile.videoFrameWidth,
                mProfile.videoFrameHeight);
        
        // 設(shè)置錄制會話的最長持續(xù)時間(以ms為單位)
        mMediaRecorder.setMaxDuration(mMaxVideoDurationInMs);
        
        // 設(shè)置視頻幀捕獲率
        mMediaRecorder.setCaptureRate(fps);

        Location loc = mLocationManager.getCurrentLocation();
        if (loc != null) {
            // 設(shè)置并存儲在輸出文件中的地理數(shù)據(jù)(經(jīng)度和緯度)
            mMediaRecorder.setLocation((float) loc.getLatitude(),
                    (float) loc.getLongitude());
        }

        // *必要* 設(shè)置視頻輸出文件的路徑
        mMediaRecorder.setOutputFile(mVideoFilename);

        // Set maximum file size.
        try {
            // 設(shè)置錄制文件的最大文件大小
            mMediaRecorder.setMaxFileSize(maxFileSize);
        } catch (RuntimeException exception) {
            // We are going to ignore failure of setMaxFileSize here, as
            // a) The composer selected may simply not support it, or
            // b) The underlying media framework may not handle 64-bit range
            // on the size restriction.
        }

        // 輸出旋轉(zhuǎn)90度晶默,保持豎屏錄制
        mMediaRecorder.setOrientationHint(90);

        try {
            // *必要* 準(zhǔn)備錄制
            mMediaRecorder.prepare();
        } catch (IOException e) {
            // prepare failed
            // releasing media recorder
            if (mMediaRecorder != null) {
                mMediaRecorder.reset();
                mMediaRecorder.release();
                mMediaRecorder = null;
            }
            throw new RuntimeException(e);
        }

        // 注冊一個用于記錄錄制時出現(xiàn)錯誤的監(jiān)聽器
        mMediaRecorder.setOnErrorListener(this);
        
        // 注冊一個用于記錄錄制時出現(xiàn)的信息事件的監(jiān)聽器
        mMediaRecorder.setOnInfoListener(this);
    }


停止錄像

    private boolean stopVideoRecording(boolean shouldSaveVideo, boolean checkStorage) {
        ...

        boolean fail = false;

        try {
            mMediaRecorder.setOnErrorListener(null);
            mMediaRecorder.setOnInfoListener(null);

            // *必要* 停止錄制
            mMediaRecorder.stop();
            ...
        } catch (RuntimeException e) {
            // stop fail delete Video File
            if (mVideoFilename != null) {
                deleteVideoFile(mVideoFilename);
            }
            fail = true;
        }

        ...

        // 保存錄像
        saveVideo();

        if (mMediaRecorder != null) {
            /**
             * 必要
             * 重置多媒體狀態(tài),調(diào)用該方法之后之前的所有 MediaRecorder configuration 將被移除
             * 如果還想再次錄像为流,需要再次配置  MediaRecorder configuration
             */
            mMediaRecorder.reset();
            // *必要* 釋放多媒體資源
            mMediaRecorder.release();
            mMediaRecorder = null;
        }
        ...

        // *必要* 給 Camera 硬件加鎖
        mCameraDevice.lock();

        ...

        return fail;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末呕屎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子敬察,更是在濱河造成了極大的恐慌秀睛,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件莲祸,死亡現(xiàn)場離奇詭異蹂安,居然都是意外死亡,警方通過查閱死者的電腦和手機锐帜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門田盈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缴阎,你說我怎么就攤上這事允瞧。” “怎么了蛮拔?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵述暂,是天一觀的道長。 經(jīng)常有香客問我建炫,道長畦韭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任肛跌,我火速辦了婚禮廊驼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惋砂。我一直安慰自己,他們只是感情好绳锅,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布西饵。 她就那樣靜靜地躺著,像睡著了一般鳞芙。 火紅的嫁衣襯著肌膚如雪眷柔。 梳的紋絲不亂的頭發(fā)上期虾,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音驯嘱,去河邊找鬼镶苞。 笑死,一個胖子當(dāng)著我的面吹牛鞠评,可吹牛的內(nèi)容都是我干的茂蚓。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼剃幌,長吁一口氣:“原來是場噩夢啊……” “哼聋涨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起负乡,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤牍白,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后抖棘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茂腥,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年切省,在試婚紗的時候發(fā)現(xiàn)自己被綠了最岗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡数尿,死狀恐怖仑性,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情右蹦,我是刑警寧澤诊杆,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站何陆,受9級特大地震影響晨汹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贷盲,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一淘这、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巩剖,春花似錦铝穷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鞠鲜,卻和暖如春宁脊,著一層夾襖步出監(jiān)牢的瞬間断国,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工榆苞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留稳衬,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓坐漏,卻偏偏與公主長得像薄疚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子仙畦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,527評論 25 707
  • 不忍心早上太冷 不容許中午太熱 把日子過成相同時間输涕,相同地點, 一段 知心的告白 用一段情 贈一個人 為一個人 守...
    江城妖怪閱讀 164評論 0 0
  • 一年前的慨畸,色彩漸漸淡去~
    無業(yè)遊民閱讀 230評論 0 1
  • 這幾天過的跟做夢一樣莱坎,似乎到現(xiàn)在才完全清醒,值得慶幸的是昨晚沒有再害怕到失眠寸士,能一夜安睡檐什,但是還是不敢去關(guān)燈,不敢...
    詩經(jīng)丫丫閱讀 155評論 0 0
  • 快過年了弱卡,我想在回家之前買幾本書看看乃正。這個時候,我忽想起老家的那些書店婶博。老家所在的縣城叫化州瓮具,曾聽說是全國的八強縣...
    染的人閱讀 211評論 0 0