1. 程式人生 > >Android錄製小視訊(仿微信小視訊)

Android錄製小視訊(仿微信小視訊)

Android錄製小視訊

一、概述

日常生活中,錄製一些視訊已經漸漸成為一種習慣,當然這對於我們技術來說並沒有什麼影響,因為無論大家用不用,你都需要開發,這只是需求制定者–PM應該關心的事情,我們需要關心的是視訊開發的過程以及難點還有會碰上什麼坑,這才是技術應該想的事情。不過,市場上面的視訊以及直播的App確實也是與日俱增,蝌蚪音客、美拍、小影,小咖秀,快手等等。這類App的技術難點基本都是在音視訊處理這一塊,iOS對多媒體處理的支援比較豐富,但是Android就會差很多,不是總有著ios程式設計師的一句話:“我們系統支援呀……”,這時候你除了心裡呵呵,好像也沒有別的辦法了哈,那麼作為Android的屌絲,還是老老實實研究一下自己該怎麼去爬坑吧。

二、型別

大家都知道Android原生錄製比例是1:1,當然這並不是說我們萬能的Android開發者就沒有別的渠道,下面就是比較高大上的用法:

1.這時候大可以放棄原生的介面,使用FFmpeg和OpenCV進行錄製,但是缺點也是明顯的,多機型相容複雜並且要求開發者一定程度的C語言功底,但是最難解決的問題是效能問題,FFmeg和OpenCV都是開源方案,如果要真正達實用級別往往還需要優化定製,這對於熟練於做Android展現的開發者來說完全就是一個新的領域。

2.使用原生API錄製,做一些遮罩等處理,例如微信未更新錢的小視訊,就是這樣的展現形式,當然正常的錄製就沒什麼大問題了,就是各種適配麻煩點,其他的都好說,而且對於開發者來說也能更好的接受。

三、視訊錄製
(一)、第一種方式可以直接呼叫系統錄影

         // 啟用系統的照相機進行錄影
        Intent intent = new Intent();
        intent.setAction("android.media.action.VIDEO_CAPTURE");
        intent.addCategory("android.intent.category.DEFAULT");
        // 保存錄像到指定的路徑
        File file = new File("/sdcard/video.3pg");
        Uri uri = Uri.fromFile(file);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        startActivityForResult(intent, 0
);

(二)、當然大部分需求都需要自己定義並且修改某些屬性,下面著重說一下自定義寫法。
1.自定義錄影佈局

<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/background_dark"
        android:orientation="vertical">

        <SurfaceView
            android:id="@+id/surfaceview"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />

        <ProgressBar
            android:id="@+id/progressBar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="3dp" />
        </LinearLayout>   

surfaceView主要使用來顯示錄製視訊的,progressbar顯示進度條。

2.定義自定義RecordVideo

@SuppressLint("NewApi")
    public RecordVideo(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // 初始化各項元件
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RecordVideo, defStyle, 0);
        mWidth = typedArray.getInteger(R.styleable.RecordVideo_witdh, 320);// 預設320
        mHeight = typedArray.getInteger(R.styleable.RecordVideo_height, 240);// 預設240
        isOpenCamera = typedArray.getBoolean(R.styleable.RecordVideo_open_camera, true);// 預設開啟
        mRecordMaxTime=typedArray.getInteger(R.styleable.RecordVideo_timeLenght,60);//預設為10

        LayoutInflater.from(context).inflate(R.layout.movie_recorder_view, this);
        mSurfaceView = (SurfaceView) findViewById(R.id.surfaceview);
        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
        mProgressBar.setMax(mRecordMaxTime);// 設定進度條最大量

        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(new CustomCallBack());
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        //釋放資源
        typedArray.recycle();
    }

初始化使用的元件,把第一步surfaceView設定進來

3.定義一個CallBack,並且初始化攝像頭這個必備資源

 private class CustomCallBack implements SurfaceHolder.Callback {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            if (!isOpenCamera)
                return;
            initCamera();
        }

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

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            if (!isOpenCamera)
                return;
            releaseCameraResource();
        }
    }
     //初始化攝像
    @SuppressLint("NewApi")
    public void initCamera()  {
        if (mCamera != null) {
            releaseCameraResource();
        }
        try {
            mCamera = Camera.open();
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewDisplay(mSurfaceHolder);
        } catch (Exception e) {
            //提示使用者許可權並且釋放建立資源
            releaseCameraResource();
        }
        if (mCamera == null)
            return;
        mCamera.startPreview();
        mCamera.unlock();
    }
   //釋放攝像頭資源
    private void releaseCameraResource() {
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.lock();
            mCamera.release();
            mCamera = null;
        }
    }

4.進入主題,初始化攝像一些基本引數

//初始化攝像頭基本引數
    private void initRecord() {
        mMediaRecorder = new MediaRecorder();
        mMediaRecorder.reset();
        if (mCamera != null)
            mMediaRecorder.setCamera(mCamera);
        mMediaRecorder.setOnErrorListener(this);
        mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//視訊源
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//音訊源
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//視訊輸出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//音訊格式
        mMediaRecorder.setVideoSize(mWidth, mHeight);//設定解析度
        //設定幀頻率,可以按照需求適當調整,這個直接相關錄製視訊的大小
        mMediaRecorder.setVideoEncodingBitRate(1 * 1024 * 1024);
        mMediaRecorder.setOrientationHint(90);// 輸出旋轉90度,保持豎屏錄製
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);// 視訊錄製格式
        mMediaRecorder.setOutputFile(mRecordFile.getAbsolutePath());
        try {
            mMediaRecorder.prepare();
            mMediaRecorder.start();
        } catch (Exception e) {
            //提示錄製許可權存在問題,開啟相關許可權
        }
    }

上面比較注意的一個是設定幀頻率,這個會影響視訊錄製後的大小;一個是MPEG_4格式,其實就是MP4格式,另一個就是H264格式,這個跟編解碼有關,經過測試,設定H264格式會使Android跟ios通用,其他格式會出現視訊在ios上面無法播放的情況,望大家多多注意。

5.下面就進入激動人心的錄製視訊的時刻,這裡面產生的坑後面會說明一些碰到的

    /**
     * 開始錄製視訊
     * @param onRecordFinishListener 達到指定時間之後回撥介面
     */
    public void record(OnRecordFinishListener onRecordFinishListener) {
        this.mOnRecordFinishListener = onRecordFinishListener;
        createRecordDir();
        if (!isOpenCamera)// 如果未開啟攝像頭,則開啟
            initCamera();
        initRecord();
        mTimeCount = 0;// 時間計數器重新賦值
        mTimer = new Timer();
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                mTimeCount++;
                mProgressBar.setProgress(mTimeCount);// 設定進度條
                if (mTimeCount == mRecordMaxTime) {// 達到指定時間,停止拍攝
                    stop();
                    if (mOnRecordFinishListener != null)
                        mOnRecordFinishListener.onRecordFinish();
                }
            }
        }, 0, 1000);
    }
//建立錄製檔案存放的資料夾以及錄製檔名稱
    private void createRecordDir() {
        File sampleDir = new File(Environment.getExternalStorageDirectory() + File.separator + "demo/video/");
        if (!sampleDir.exists()) {
            sampleDir.mkdirs();
        }
        File vecordDir = sampleDir;
        // 建立檔案
        try {
            mRecordFile = File.createTempFile("example", ".mp4", vecordDir); //mp4格式
        } catch (IOException e) {
            //開啟檔案操作相關許可權
        }
    }

錄製時候切記要判斷下使用者攝像頭的許可權,假如使用者在錄製前關閉許可權會導致crash的問題,所以判斷下,防止誤操作的產生

7.停止錄製

//停止錄製
public void stop() {
        stopRecord();
        releaseRecord();
        releaseCameraResource();
    }
//停止錄製
    public void stopRecord() {
        mProgressBar.setProgress(0);
        if (mTimer != null)
            mTimer.cancel();
        if (mMediaRecorder != null) {
            // 設定後不會崩
            mMediaRecorder.setOnErrorListener(null);
            try {
                mMediaRecorder.stop();
            } catch (Exception e) {
                return;
            }
            mMediaRecorder.setPreviewDisplay(null);
        }
    }
//釋放資源
    private void releaseRecord() {
        if (mMediaRecorder != null) {
            mMediaRecorder.setOnErrorListener(null);
            try {
                mMediaRecorder.release();
            } catch (Exception e) {
                return;
            }
        }
        mMediaRecorder = null;
    }

停止錄製時候清空一些必要的資源,減輕App的負擔,停止時候記得設定錯誤監聽,做不做操作可以自己進行設定,但是一定要設定,否則系統不會回撥,直接異常。

8.相關的其他方法以及介面

    public int getTimeCount() {
        return mTimeCount;
    }

    public File getmRecordFile() {
        return mRecordFile;
    }

    //錄製完成回撥介面
    public interface OnRecordFinishListener {
        public void onRecordFinish();
    }

    @Override
    public void onError(MediaRecorder mr, int what, int extra) {
        try {
            if (mr != null)
                mr.reset();
        } catch (Exception e) {
            //如果錄製有錯誤,需要做相關操作來提醒使用者
        }
    }

9.最後設定Activity就可以進行視訊錄製了

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#64000000">

    <RelativeLayout
        android:id="@+id/record_video"
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:background="@color/common_black"
        android:layout_alignParentBottom="true">

        <Button
            android:id="@+id/shoot_button"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true"
            android:background="@drawable/bg_movie_add" />
    </RelativeLayout>

    <com.demo.RecordVideo
        android:id="@+id/movieRecorderView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/record_video" />
</RelativeLayout>

Activity裡面按鈕處理程式碼,這裡主要需要主要需要onTouch進行監聽,因為可能會出現短暫的按下立馬鬆開,這樣是需要做處理的

mRecorderView = (RecordVideo) findViewById(R.id.movieRecorderView);
        mShootBtn = (Button) findViewById(R.id.shoot_button);
        mShootBtn.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                        mRecorderView.record(new RecordVideo.OnRecordFinishListener() {
                            @Override
                            public void onRecordFinish() {
                                handler.sendEmptyMessage(1);
                            }
                        });
                    } else if (event.getAction() == MotionEvent.ACTION_UP) {
                        if (mRecorderView.getTimeCount() > 1)
                            handler.sendEmptyMessage(1);
                        else {
                            if (mRecorderView.getmRecordFile() != null) {
                                mRecorderView.getmRecordFile().delete();
                            }
                            mRecorderView.stop();
                            mRecorderView.initCamera();
                            Toast.makeText(RecordVideoActivity.this,"視訊錄製時間太短", Toast.LENGTH_SHORT).show();
                        }
                    }   
                }
            }
private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            finishActivity();
        }
    };

    private void finishActivity() {
        if (isFinish) {
            mRecorderView.stop();
            // 返回到播放頁面
            Intent intent = new Intent();
            intent.putExtra("path", mRecorderView.getmRecordFile().getAbsolutePath());
            setResult(RESULT_OK, intent);
        }
        finish();
    }

這裡面可能會碰到的問題,在過程中已經描述了幾個需要注意的點,後面還有比較難搞的點就是許可權,這個可以參考下許可權檢查

專案地址github
最後,歡迎討論的小夥伴。
個人郵箱: [email protected]

相關推薦

Android錄製視訊(仿視訊)

Android錄製小視訊 一、概述 日常生活中,錄製一些視訊已經漸漸成為一種習慣,當然這對於我們技術來說並沒有什麼影響,因為無論大家用不用,你都需要開發,這只是需求制定者–PM應該關心的事情,我們需要關心的是視訊開發的過程以及難點還有會碰上什麼坑,這才是技術

程式--仿程式朋友圈Pro(內容釋出、點贊、評論、回覆評論)

#### 微信小程式--仿微信小程式朋友圈Pro(內容釋出、點贊、評論、回覆評論) 專案開源地址[M朋友圈Pro 求個Star](https://gitee.com/Kindear/CloudUI) > 專案背景 ​ 基於原來的開源專案 [微信小程式仿朋友圈功能開發(釋出、點贊、評論等功能)](htt

Android 仿視訊錄製

這篇博文是借鑑下面這篇博文的,這篇播放對於整個專案的架構寫的比較清晰。這篇博文寫的很詳細,博主也是非常用心的分享自己的心得以及遇到的問題,希望對於想了解視訊錄製的博友們有所幫助。以上兩種方式都是使用原生的視訊錄製,所以檔案特別大,使用性不強,但是可以瞭解這樣一個技術,下面這個

21小時精通程序開發(仿貓眼電影App、程序問答)|程序開發視頻教程

小程序開發 結構化 pan 即使 準備 ofo 入門 sha 小時 21小時精通微信小程序開發(仿貓眼電影App、微信小程序問答)網盤地址:https://pan.baidu.com/s/1GTpPX4A1U-w_3i6k7lLztQ 密碼: 5pcz備用地址(騰訊微雲):

-齊梟飛前端架構師 程式--仿 QQ左劃事件--

廢話不多說,直接上程式碼: js: var app = getApp() Page({ data: { items: [], startX: 0, //開始座標 startY: 0 }, onLoad: func

「騰訊視訊程式外掛介紹

上期,我們在《從原理到應用,一文帶你瞭解小程式外掛能力》一文中介紹了小程式外掛的意義、作用以及應用。今天開始,我們會每期與大家分享一款優秀的小程式外掛,從使用場景到使用方法,都將作出詳細的介紹。 第一期與大家分享的小程式外掛,是「騰訊視訊」外掛。 「騰訊視訊」外

程式仿語音

搞了這麼長時間,第一次寫文章。給大家分享個技術難度不高的,主要展示錄音功能。 先看一下效果 就是這樣,點選底部按鈕會產生變化,根據bind:touchstart在觸控時觸發開始錄音事件,根據bind:touchend會在你結束觸控的時候呼叫錄音結束事件,產生一個臨時url。並且根

同一個app不同activity顯示多工(仿程式切換效果)

簡書地址:https://www.jianshu.com/p/a8f695841008 轉載請註明出處 如題,這種效果類似微信小程式顯示的效果,就是開啟微信跳一跳後,切換安卓多工視窗(就是清理記憶體視窗),會看到如下頁面 微信小程式會在其中顯示兩個單獨

店鋪無縫對接一號旺鋪程序 盤點程序新增功能

如何快速 被人 程序兼容 推送 模糊搜索 絕地反擊 作用 電商 通過 前段時間,筆者寫過一篇《不開發,商家店鋪如何快速接入微信一號旺鋪小程序》的文章。結果反響平平,無人問津。所以,筆者就在想,也許小程序真的大勢已去。  盤點迄今為止微信小程序增加的功能:價值提升,商家福音到

Android-通過SlidingMenu高仿6.2最新版手勢滑動返回(二)

word 代碼下載 extend ride 方法 最新版 roi library fin 轉載請標明出處: http://blog.csdn.net/hanhailong726188/article/details/46453627 本

android之使用GridView+仿圖片上傳功能(附源代碼)

相冊 ada nbu [] for round pen fromfile idt   由於工作要求最近在使用GridView完成圖片的批量上傳功能,我的例子當中包含仿微信圖片上傳、拍照、本地選擇、相片裁剪等功能,如果有需要的朋友可以看一下,希望我的實際經驗能對您有所幫助。

程序】程序實現各種特效實例

負責 tab php 微信 點擊 space pic 效果圖 前後端 寫在前面 最近在負責一個微信小程序的前端以及前後端接口的對接的項目,整體上所有頁面的布局我都已經搭建完成,裏面有一些常用的特效,總結一下,希望對大家和我都能有所幫助 實例1:滾動tab選項卡 先看一下效果

程序】程序之地圖功能

城市 comment olt delphi 天氣 posit truct 變量 綁定 轉載請註明出處:http://blog.csdn.net/crazy1235/article/details/55004841 基本使用 地圖組件使用起來也很簡單。 .wxml <m

[程序開發] 程序內嵌網頁web-view開發教程

工具 內容 不支持 clas .html bind har 開發 style 為了便於開發者靈活配置小程序,微信小程序開放了內嵌網頁能力。這意味著小程序的內容不再局限於pages和large,我們可以借助內嵌網頁豐富小程序的內容。下面附上詳細的開發教程(含視頻操作以及註意事

如何讓精準用戶知道我們的程序?程序帶給門店商家什麽?

IT 頁面 自身 無需 動態 電商 使用 互聯 源碼 在4月的騰訊“互聯網+”數字峰會上,微信透露了一個數據:能夠切實解決用戶痛點的小程序七日留存接近30%。而這類微信小程序基本都已經讓用戶完成了品牌認知,用戶會從微信任務欄或者歷史列表中主動找到它們,然後重復使用。 小程

程序】程序綁定企業後怎樣獲取到用戶

臨時 ces code secret 企業微信 AC inf 小程序 ram 一、獲取access_token 1、https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRECT C

Okam(奧卡姆):程式開發框架,支援百度程式、程式、支付寶程式

Okam(奧卡姆):小程式開發框架,支援百度小程式、微信小程式、支付寶小程式 Okam 是什麼 `Okam` 一個面向小程式開發的開發框架,開發體驗類 `Vue`。詳情 Okam 對各小程式的支援情況 支援 百度小程式 支援 微信小程式 支援 支付寶小程式 Okam 提供

程式-day02-程式-框架-配置-pages、windows、tabBars、debug(json檔案)

1.pages(頁面路徑列表) "pages":[ "pages/index/index", "pages/do/do", "pages/eat/eat", "pages/logs/logs" ] 2.windows(全域性的預設視窗表現) "window":{

技術白之程式的圖片加文字連結

在多彩的圖片呈現下的程式必不可缺的便是文字的搭配,圖片勾起興趣,文字輔助表達,多數情況下我們上傳的圖片都需要在它周圍添上合適的標題,以便美觀和表述清晰。下面是簡單的圖片文字連結的截圖: 說到圖片和文字的連結就不得不理下思路:首先我想要在小程式內顯示圖片文字資訊,且在點選目標圖片或文字時,可

淺談支付寶程式與程式開發的區別

淺談支付寶小程式與微信小程式開發的區別 一、app.json (1)設定小程式通用的的狀態列、導航條、標題、視窗背景色 支付寶小程式 "window": { "defaultTitle": "病案到家", //頁面標題 "titleBarColor": "#1