Запись живой обработки OpenCV на Android

Моя цель - сделать пару вещей:

  1. Используйте OpenCV и JavaCameraView для обработки кадров с камеры телефона.
  2. Включить запись этого обработанного видео, как это происходит

У меня они оба работают, но то, как мне пришлось реализовать номер 2, просто смешно:

  1. Для каждого кадра запишите обработанный мат в виде файла изображения.
  2. Когда запись остановится, используйте библиотеку JCodec для Android, чтобы объединить их в видеофайл.

Это работает, но имеет массу недостатков: частота кадров невыносимо падает во время записи, шаг сшивания занимает около полсекунды на кадр, и не хватает памяти для видео продолжительностью более пары секунд — и это после Я уменьшаю разрешение камеры, чтобы изображения были как можно меньше. Даже в этом случае частота кадров видео не соответствует реальности, и видео выглядит безумно ускоренным.

Это кажется нелепым по многим причинам, поэтому мой вопрос: есть ли лучший способ сделать это?

Вот небольшой пример, если кто-то хочет запустить его. Для этого требуется проект OpenCV для Android, доступный здесь, и проект JCodec для Android, доступный здесь.

Манифест.xml:

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="22"
/>

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >

    <activity
        android:name=".MainActivity"
        android:screenOrientation="landscape"
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

</application>

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

MainActivity:

package com.example.videotest;

import java.io.File;
import java.util.List;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;

import android.app.Activity;
import android.media.MediaScannerConnection;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;

public class MainActivity extends Activity implements CvCameraViewListener2{

    private CameraView cameraView;
    private Mat edgesMat;
    private final Scalar greenScalar = new Scalar(0,255,0);
    private int resolutionIndex = 0;
    private MatVideoWriter matVideoWriter = new MatVideoWriter();


    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
            case LoaderCallbackInterface.SUCCESS:
            {
                Log.i("VideoTest", "OpenCV loaded successfully");

                cameraView.enableView();

            } break;
            default:
            {
                super.onManagerConnected(status);
            } break;
            }
        }
    };


    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_main);

        cameraView = (CameraView) findViewById(R.id.cameraView);
        cameraView.setVisibility(SurfaceView.VISIBLE);
        cameraView.setCvCameraViewListener(this);
    }

    @Override
    public void onPause()
    {
        super.onPause();
        if (cameraView != null){
            cameraView.disableView();
        }
    }

    @Override
    public void onResume()
    {
        super.onResume();
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2
package com.example.videotest;

import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.jcodec.api.android.SequenceEncoder;
import org.opencv.core.Mat;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

public class MatVideoWriter {

    boolean recording;
    File dir;
    int imageIndex = 0;

    public void start(File dir){
        this.dir = dir;
        recording = true;
    }

    public void stop(){
        recording = false;

        try{
            File file = new File(dir, "video.mp4");
            SequenceEncoder encoder = new SequenceEncoder(file);

            List<File> files = Arrays.asList(dir.listFiles());
            Collections.sort(files, new Comparator<File>(){
                @Override
                public int compare(File lhs, File rhs) {
                    return lhs.getName().compareTo(rhs.getName());
                }
            });

            for(File f : files){
                Log.i("VideoTest", "Encoding image: " + f.getAbsolutePath());
                try{
                    Bitmap frame = BitmapFactory.decodeFile(f.getAbsolutePath());
                    encoder.encodeImage(frame);
                }
                catch(Exception e){
                    e.printStackTrace();
                }

            }
            encoder.finish();
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }

    public void write(Mat mat){

        //convert from BGR to RGB
        Mat rgbMat = new Mat();
        Imgproc.cvtColor(mat, rgbMat, Imgproc.COLOR_BGR2RGB);

        File file = new File(dir, "img" + imageIndex + ".png");

        String filename = file.toString();
        boolean success = Highgui.imwrite(filename, rgbMat);

        Log.i("VideoTest", "Success writing img" + imageIndex +".png: " + success);

        imageIndex++;
    }

    public boolean isRecording() {
        return recording;
    }
}
3, this, mLoaderCallback); } public void onDestroy() { super.onDestroy(); if (cameraView != null) cameraView.disableView(); } public void onCameraViewStarted(int width, int height) { edgesMat = new Mat(); } public void onCameraViewStopped() { if (edgesMat != null) edgesMat.release(); edgesMat = null; } public Mat onCameraFrame(CvCameraViewFrame inputFrame) { Mat rgba = inputFrame.rgba(); org.opencv.core.Size sizeRgba = rgba.size(); int rows = (int) sizeRgba.height; int cols = (int) sizeRgba.width; int left = cols / 8; int top = rows / 8; int width = cols * 3 / 4; int height = rows * 3 / 4; //get sub-image Mat rgbaInnerWindow = rgba.submat(top, top + height, left, left + width); //create edgesMat from sub-image Imgproc.Canny(rgbaInnerWindow, edgesMat, 100, 100); Mat colorEdges = new Mat(); Mat killMe = colorEdges; edgesMat.copyTo(colorEdges); Imgproc.cvtColor(colorEdges, colorEdges, Imgproc.COLOR_GRAY2BGRA); colorEdges = colorEdges.setTo(greenScalar, edgesMat); colorEdges.copyTo(rgbaInnerWindow, edgesMat); killMe.release(); colorEdges.release(); rgbaInnerWindow.release(); if(matVideoWriter.isRecording()){ matVideoWriter.write(rgba); } return rgba; } public void changeResolution(View v){ List<android.hardware.Camera.Size> cameraResolutionList = cameraView.getResolutionList(); resolutionIndex++; if(resolutionIndex >= cameraResolutionList.size()){ resolutionIndex = 0; } android.hardware.Camera.Size resolution = cameraResolutionList.get(resolutionIndex); cameraView.setResolution(resolution.width, resolution.height); resolution = cameraView.getResolution(); String caption = Integer.valueOf(resolution.width).toString() + "x" + Integer.valueOf(resolution.height).toString(); Toast.makeText(this, caption, Toast.LENGTH_SHORT).show(); } public void startVideo(View v){ if(matVideoWriter.isRecording()){ matVideoWriter.stop(); File file = new File(getExternalFilesDir(null), "VideoTest/images/"); for(String img : file.list()){ String scanMe = new File(file, img).getAbsolutePath(); MediaScannerConnection.scanFile(this, new String[]{scanMe}, null, null); Log.i("VideoTest", "Scanning: " +scanMe); } file = new File(file, "video.mp4"); MediaScannerConnection.scanFile(this, new String[]{file.getAbsolutePath()}, null, null); } else{ String state = Environment.getExternalStorageState(); Log.i("VideoTest", "state: " + state); File ext = getExternalFilesDir(null); Log.i("VideoTest", "ext: " + ext.getAbsolutePath()); File file = new File(getExternalFilesDir(null), "VideoTest/images/"); if(!file.exists()){ boolean success = file.mkdirs(); Log.i("VideoTest", "mkdirs: " + success); } else{ Log.i("VideoTest", "file exists."); } Log.i("VideoTest", "starting recording: " + file.getAbsolutePath()); matVideoWriter.start(file); } } }

Вид камеры:

package com.example.videotest;

import java.io.FileOutputStream;
import java.util.List;

import org.opencv.android.JavaCameraView;

import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.util.AttributeSet;
import android.util.Log;

public class CameraView extends JavaCameraView{

    private String mPictureFileName;

    public CameraView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public List<String> getEffectList() {
        return mCamera.getParameters().getSupportedColorEffects();
    }

    public boolean isEffectSupported() {
        return (mCamera.getParameters().getColorEffect() != null);
    }

    public String getEffect() {
        return mCamera.getParameters().getColorEffect();
    }

    public void setEffect(String effect) {
        Camera.Parameters params = mCamera.getParameters();
        params.setColorEffect(effect);
        mCamera.setParameters(params);
    }

    public List<android.hardware.Camera.Size> getResolutionList() {
        return mCamera.getParameters().getSupportedPreviewSizes();
    }

    public void setResolution(int width, int height) {
        disconnectCamera();
        mMaxHeight = height;
        mMaxWidth = width;
        connectCamera(getWidth(), getHeight());
    }

    public android.hardware.Camera.Size getResolution() {
        return mCamera.getParameters().getPreviewSize();
    }

}

МатВидеоСценарист:

package com.example.videotest;

import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.jcodec.api.android.SequenceEncoder;
import org.opencv.core.Mat;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

public class MatVideoWriter {

    boolean recording;
    File dir;
    int imageIndex = 0;

    public void start(File dir){
        this.dir = dir;
        recording = true;
    }

    public void stop(){
        recording = false;

        try{
            File file = new File(dir, "video.mp4");
            SequenceEncoder encoder = new SequenceEncoder(file);

            List<File> files = Arrays.asList(dir.listFiles());
            Collections.sort(files, new Comparator<File>(){
                @Override
                public int compare(File lhs, File rhs) {
                    return lhs.getName().compareTo(rhs.getName());
                }
            });

            for(File f : files){
                Log.i("VideoTest", "Encoding image: " + f.getAbsolutePath());
                try{
                    Bitmap frame = BitmapFactory.decodeFile(f.getAbsolutePath());
                    encoder.encodeImage(frame);
                }
                catch(Exception e){
                    e.printStackTrace();
                }

            }
            encoder.finish();
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }

    public void write(Mat mat){

        //convert from BGR to RGB
        Mat rgbMat = new Mat();
        Imgproc.cvtColor(mat, rgbMat, Imgproc.COLOR_BGR2RGB);

        File file = new File(dir, "img" + imageIndex + ".png");

        String filename = file.toString();
        boolean success = Highgui.imwrite(filename, rgbMat);

        Log.i("VideoTest", "Success writing img" + imageIndex +".png: " + success);

        imageIndex++;
    }

    public boolean isRecording() {
        return recording;
    }
}

Изменить: я не получил никаких комментариев или ответов, поэтому я отправил сообщение на форум OpenCV здесь.


person Kevin Workman    schedule 22.02.2015    source источник
comment
Почему бы вам не использовать стандартную запись активности, встроенную в Android? ? После этого вы можете получить видеофайл и сделать с ним все, что захотите.   -  person Ha Dang    schedule 26.02.2015
comment
@HaDang Потому что мне нужно выполнять обработку вживую, а не после записи. Запись по умолчанию не дает вам доступа к отдельным кадрам.   -  person Kevin Workman    schedule 26.02.2015
comment
Как насчет помещения входящих изображений в очередь после обработки и периодической передачи очереди другому потоку для сохранения?   -  person Ha Dang    schedule 26.02.2015
comment
@HaDang Думаю, я мог бы это сделать, но это все еще кажется сумасшедшим решением. Я был очень удивлен, обнаружив, что не существует стандартного способа экспорта видео, поскольку оно обрабатывается таким образом. Похоже, это довольно важная функция для разработчиков OpenCV.   -  person Kevin Workman    schedule 26.02.2015
comment
@Kenvin Workman: Вы пробовали VideoWriter? Похоже, что кому-то удалось сгенерировать привязку Java.   -  person Ha Dang    schedule 26.02.2015
comment
@HaDang К сожалению, VideoWriter не работает на Android, поэтому я выбрал jcodec: answers.opencv.org/question/29068/ (также отмечу, что экспорт изображений в другой поток не решит проблемы с нехваткой памяти JCodec)   -  person Kevin Workman    schedule 26.02.2015
comment
Давайте продолжим обсуждение в чате.   -  person Ha Dang    schedule 26.02.2015
comment
Это требование? Использование класса JavaCameraView и Android SDK для OpenCV? Обрабатывающую часть кода лучше реализовать на C++ с OpenCV и отправлять ему массив байтов   -  person carlos.baez    schedule 03.03.2015


Ответы (4)


Я решил аналогичную проблему, создав MediaRecorder и передав его в OpenCV CameraBridgeViewBase, который я изменил следующим образом.

protected MediaRecorder mRecorder;
protected Surface mSurface = null;

public void setRecorder(MediaRecorder rec) {
    mRecorder = rec;
    if (mRecorder != null) {
        mSurface = mRecorder.getSurface();
    }

а также

protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
    Mat modified;

    if (mListener != null) {
        modified = mListener.onCameraFrame(frame);
    } else {
        modified = frame.rgba();
    }

    boolean bmpValid = true;
    if (modified != null) {
        try {
            Utils.matToBitmap(modified, mCacheBitmap);
        } catch(Exception e) {
            Log.e(TAG, "Mat type: " + modified);
            Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
            Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
            bmpValid = false;
        }
    }

    if (bmpValid && mCacheBitmap != null) {
        Canvas canvas;

        if (mRecorder != null) {
            canvas = mSurface.lockCanvas(null);

            canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
            Log.d(TAG, "mStretch value: " + mScale);

            if (mScale != 0) {
                canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                     new Rect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2),
                     (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2),
                     (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()),
                     (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2 + mScale*mCacheBitmap.getHeight())), null);
            } else {
                 canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                     new Rect((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,
                     (canvas.getHeight() - mCacheBitmap.getHeight()) / 2,
                     (canvas.getWidth() - mCacheBitmap.getWidth()) / 2 + mCacheBitmap.getWidth(),
                     (canvas.getHeight() - mCacheBitmap.getHeight()) / 2 + mCacheBitmap.getHeight()), null);
            }

            if (mFpsMeter != null) {
                mFpsMeter.measure();
                mFpsMeter.draw(canvas, 20, 30);
            }
            mSurface.unlockCanvasAndPost(canvas);
        } 

    }

    ...

}

Я оставил исходную часть deliverAndDrawFrame как есть, чтобы она продолжала отображать вывод на исходную поверхность. Таким образом, я могу обрабатывать изображения с камеры, реализуя onCameraFrame в MainActivity, и сохранять полученные изображения в видео без необходимости в ffmpeg.

ИЗМЕНИТЬ Я настроил MediaRecorder следующим образом.

recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);

CamcorderProfile cpHigh = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
recorder.setProfile(cpHigh);
recorder.setOutputFile("out.mp4");
recorder.setVideoSize(mOpenCvCameraView.mFrameWidth, mOpenCvCameraView.mFrameHeight);

recorder.setOnInfoListener(this);
recorder.setOnErrorListener(this);
recorder.prepare();

зарегистрируйте его в OpenCvCameraView

mOpenCvCameraView.setRecorder(recorder);

и начать запись

recorder.start();
person SimonFojtu    schedule 12.10.2015
comment
Модифицированный холст также записывается? Я попробовал ваше решение, но не получил удовольствия - person John M; 03.04.2016
comment
Да, в этом вся суть. Когда установлено mRecorder в CameraBridgeViewBase, холст передается в mRecorder для записи. - person SimonFojtu; 04.04.2016
comment
Это восхитительно. Я потратил около 5 часов, пытаясь реализовать это вчера, но безрезультатно. Я очень запутался в классе MediaRecorder, так как не могу найти много примеров — когда я устанавливаю объект MediaRecorder для использования поверхности, устанавливаю ли я предварительный просмотр для моего измененного CameraBridgeViewBase? Я понимаю основные принципы вашего метода, но я просто застрял в понимании всего потока! - person John M; 04.04.2016
comment
Я обновил ответ инициализацией MediaRecorder. Мне тоже потребовалось некоторое время, чтобы все исправить. - person SimonFojtu; 04.04.2016
comment
Потрясающий! Не могу дождаться, чтобы попробовать это сегодня вечером! Спасибо! - person John M; 04.04.2016
comment
Получил это работает отлично! Честно говоря, это лучшее решение, и его гораздо проще реализовать по сравнению с FFmpeg. Спасибо еще раз. Просто примечание для всех, кто пытается это сделать, так как это не было очевидно для меня в начале, но вы должны рисовать на обоих холстах в методе deliveryAndDraw. - person John M; 06.04.2016
comment
Привет. У вас есть полный код где-нибудь? У меня возникли проблемы с совместной работой MediaRecorder и JavaCameraView, поскольку метод JavaCameraView mCamera защищен, и мне нужно передать его в MediaRecorder. - person ; 13.10.2016
comment
Извините, у меня больше нет доступа к исходным кодам. - person SimonFojtu; 14.10.2016
comment
Не волнуйтесь. Вдобавок к этому, не было ли у вас низкой частоты кадров из-за одновременной отрисовки двух растровых изображений? - person ; 02.11.2016
comment
Это было немного медленнее, но использовалось только тогда, когда мы хотели записать видео с экрана, что не было основной целью приложения, поэтому мы не возражали. - person SimonFojtu; 03.11.2016
comment
Предварительный просмотр камеры недоступен, появлялся только черный экран. Это тот же подход и та же проблема... mediarecorder getsurface не отображает вид "> stackoverflow.com/questions/38494160/ - person Khan; 28.08.2017
comment
этот не работает на всех устройствах. Метод mediarecorder.stop не работает. вы встречали такую ​​проблему? - person user924; 13.07.2018
comment
@JohnM, что ты имеешь в виду под обоими холстами? - person user924; 13.07.2018

@HaDang указал мне на эти ссылки:

http://www.walking-productions.com/notslop/2013/01/16/android-live-streaming-courtesy-of-javacv-and-ffmpeg/

https://code.google.com/p/javacv/source/browse/samples/RecordActivity.java

В этом примере для записи видео используется Java-оболочка FFMPEG. Этот проект является довольно полезной отправной точкой для всех, кто хочет сделать то же самое: https://github.com/vanevery/JavaCV_0.3_stream_test

Я взял этот проект выше и вбил его в свой пример. Это очень грязно, но работает:

package com.example.videotest;


import java.io.File;
import java.io.IOException;
import java.nio.ShortBuffer;
import java.util.List;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;

import com.googlecode.javacv.FFmpegFrameRecorder;
import com.googlecode.javacv.FrameRecorder.Exception;
import com.googlecode.javacv.cpp.opencv_core.IplImage;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.media.MediaScannerConnection;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;

public class MainActivity extends Activity implements CvCameraViewListener2{

    private CameraView cameraView;
    private Mat edgesMat;
    private final Scalar greenScalar = new Scalar(0,255,0);
    private int resolutionIndex = 0;

    private IplImage videoImage = null;

    boolean recording = false;
    private volatile FFmpegFrameRecorder recorder;

    private int sampleAudioRateInHz = 44100;
    private int imageWidth = 320;
    private int imageHeight = 240;
    private int frameRate = 30;

    private Thread audioThread;
    volatile boolean runAudioThread = true;
    private AudioRecord audioRecord;
    private AudioRecordRunnable audioRecordRunnable;

    private String ffmpeg_link;

    long startTime = 0;

    private String LOG_TAG = "VideoTest";

    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
            case LoaderCallbackInterface.SUCCESS:
                Log.i("VideoTest", "OpenCV loaded successfully");
                cameraView.enableView();
                break;
            default:
                super.onManagerConnected(status);
                break;
            }
        }
    };


    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_main);

        cameraView = (CameraView) findViewById(R.id.cameraView);
        cameraView.setVisibility(SurfaceView.VISIBLE);
        cameraView.setCvCameraViewListener(this);
    }

    private void initRecorder() {
        Log.w(LOG_TAG,"initRecorder");

        int depth = com.googlecode.javacv.cpp.opencv_core.IPL_DEPTH_8U;
        int channels = 4;

        // if (yuvIplimage == null) {
        // Recreated after frame size is set in surface change method
        videoImage = IplImage.create(imageWidth, imageHeight, depth, channels);
        //yuvIplimage = IplImage.create(imageWidth, imageHeight, IPL_DEPTH_32S, 2);

        Log.v(LOG_TAG, "IplImage.create");
        // }

        File videoFile = new File(getExternalFilesDir(null), "VideoTest/images/video.mp4");
        boolean mk = videoFile.getParentFile().mkdirs();
        Log.v(LOG_TAG, "Mkdir: " + mk);

        boolean del = videoFile.delete();
        Log.v(LOG_TAG, "del: " + del);

        try {
            boolean created = videoFile.createNewFile();
            Log.v(LOG_TAG, "Created: " + created);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        ffmpeg_link = videoFile.getAbsolutePath();
        recorder = new FFmpegFrameRecorder(ffmpeg_link, imageWidth, imageHeight, 1);
        Log.v(LOG_TAG, "FFmpegFrameRecorder: " + ffmpeg_link + " imageWidth: " + imageWidth + " imageHeight " + imageHeight);

        recorder.setFormat("mp4");
        Log.v(LOG_TAG, "recorder.setFormat(\"mp4\")");

        recorder.setSampleRate(sampleAudioRateInHz);
        Log.v(LOG_TAG, "recorder.setSampleRate(sampleAudioRateInHz)");

        // re-set in the surface changed method as well
        recorder.setFrameRate(frameRate);
        Log.v(LOG_TAG, "recorder.setFrameRate(frameRate)");

        // Create audio recording thread
        audioRecordRunnable = new AudioRecordRunnable();
        audioThread = new Thread(audioRecordRunnable);
    }

    @Override
    public void onPause()
    {
        super.onPause();
        if (cameraView != null){
            cameraView.disableView();
        }
    }

    @Override
    public void onResume()
    {
        super.onResume();
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
    }

    public void onDestroy() {
        super.onDestroy();
        if (cameraView != null)
            cameraView.disableView();
    }

    public void onCameraViewStarted(int width, int height) {
        edgesMat = new Mat();
    }

    public void onCameraViewStopped() {
        if (edgesMat != null)
            edgesMat.release();

        edgesMat = null;
    }

    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {

        Mat rgba = inputFrame.rgba();
        org.opencv.core.Size sizeRgba = rgba.size();

        int rows = (int) sizeRgba.height;
        int cols = (int) sizeRgba.width;

        int left = cols / 8;
        int top = rows / 8;
        int width = cols * 3 / 4;
        int height = rows * 3 / 4;

        //get sub-image
        Mat rgbaInnerWindow = rgba.submat(top, top + height, left, left + width);

        //create edgesMat from sub-image
        Imgproc.Canny(rgbaInnerWindow, edgesMat, 100, 100);

        Mat colorEdges = new Mat();
        Mat killMe = colorEdges;
        edgesMat.copyTo(colorEdges);
        Imgproc.cvtColor(colorEdges, colorEdges, Imgproc.COLOR_GRAY2BGRA);


        colorEdges = colorEdges.setTo(greenScalar, edgesMat);
        colorEdges.copyTo(rgbaInnerWindow, edgesMat);

        killMe.release();
        colorEdges.release();

        rgbaInnerWindow.release();

        if(recording){
            byte[] byteFrame = new byte[(int) (rgba.total() * rgba.channels())];
            rgba.get(0, 0, byteFrame);
            onFrame(byteFrame);
        }

        return rgba;
    }

    public void stopRecording() {
        // This should stop the audio thread from running
        runAudioThread = false;

        if (recorder != null) {
            Log.v(LOG_TAG,"Finishing recording, calling stop and release on recorder");
            try {
                recorder.stop();
                recorder.release();
            } catch (FFmpegFrameRecorder.Exception e) {
                e.printStackTrace();
            }
            recorder = null;
        }

        MediaScannerConnection.scanFile(MainActivity.this, new String[]{ffmpeg_link}, null, null);
    }


    public void changeResolution(View v){
        List<android.hardware.Camera.Size> cameraResolutionList = cameraView.getResolutionList();
        resolutionIndex++;
        if(resolutionIndex >= cameraResolutionList.size()){
            resolutionIndex = 0;
        }

        android.hardware.Camera.Size resolution = cameraResolutionList.get(resolutionIndex);
        cameraView.setResolution(resolution.width, resolution.height);
        resolution = cameraView.getResolution();
        String caption = Integer.valueOf(resolution.width).toString() + "x" + Integer.valueOf(resolution.height).toString();
        Toast.makeText(this, caption, Toast.LENGTH_SHORT).show();

        imageWidth = resolution.width;
        imageHeight = resolution.height;

        frameRate = cameraView.getFrameRate();

        initRecorder();
    }

    int frames = 0;

    private void onFrame(byte[] data){

        if (videoImage != null && recording) {
            long videoTimestamp = 1000 * (System.currentTimeMillis() - startTime);

            // Put the camera preview frame right into the yuvIplimage object
            videoImage.getByteBuffer().put(data);

            try {

                // Get the correct time
                recorder.setTimestamp(videoTimestamp);

                // Record the image into FFmpegFrameRecorder
                recorder.record(videoImage);

                frames++;

                Log.i(LOG_TAG, "Wrote Frame: " + frames);

            } 
            catch (FFmpegFrameRecorder.Exception e) {
                Log.v(LOG_TAG,e.getMessage());
                e.printStackTrace();
            }
        }

    }

    public void startVideo(View v){

        recording = !recording;

        Log.i(LOG_TAG, "Recording: " + recording);

        if(recording){
            startTime = System.currentTimeMillis();
            try {
                recorder.start();

                Log.i(LOG_TAG, "STARTED RECORDING.");

            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        else{
            stopRecording();
        }
    }



    class AudioRecordRunnable implements Runnable {

        @Override
        public void run() {
            // Set the thread priority
            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);

            // Audio
            int bufferSize;
            short[] audioData;
            int bufferReadResult;

            bufferSize = AudioRecord.getMinBufferSize(sampleAudioRateInHz, 
                    AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
            audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleAudioRateInHz, 
                    AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);

            audioData = new short[bufferSize];

            Log.d(LOG_TAG, "audioRecord.startRecording()");
            audioRecord.startRecording();

            // Audio Capture/Encoding Loop
            while (runAudioThread) {
                // Read from audioRecord
                bufferReadResult = audioRecord.read(audioData, 0, audioData.length);
                if (bufferReadResult > 0) {
                    //Log.v(LOG_TAG,"audioRecord bufferReadResult: " + bufferReadResult);

                    // Changes in this variable may not be picked up despite it being "volatile"
                    if (recording) {
                        try {
                            // Write to FFmpegFrameRecorder
                            recorder.record(ShortBuffer.wrap(audioData, 0, bufferReadResult));
                        } catch (FFmpegFrameRecorder.Exception e) {
                            Log.v(LOG_TAG,e.getMessage());
                            e.printStackTrace();
                        }
                    }
                }
            }
            Log.v(LOG_TAG,"AudioThread Finished");

            /* Capture/Encoding finished, release recorder */
            if (audioRecord != null) {
                audioRecord.stop();
                audioRecord.release();
                audioRecord = null;

                MediaScannerConnection.scanFile(MainActivity.this, new String[]{ffmpeg_link}, null, null);

                Log.v(LOG_TAG,"audioRecord released");
            }
        }
    }

}
person Kevin Workman    schedule 27.02.2015

Попробуйте скомпилировать FFMPEG для Android для обработки живого видео. Интеграция с android и openCV, поможет эта ссылка:

http://www.jayrambhia.com/blog/ffmpeg-opencv-android

person Shubham    schedule 27.02.2015
comment
Обновите ссылку, последний слэш должен быть удален. ;) - person Khan; 25.08.2017

Другой вариант, который работает только на Lollipop и Marshmallow, — использовать новый MediaProjectionManager для захвата и записи того, что происходит на экране вашего устройства. Отличный пример здесь:

http://www.mattsnider.com/video-recording-with-mediaprojectionmanager/

Он полностью независим от камеры вашего устройства и не требует доступа к ней или тому, что вы делаете с OpenCV. Он просто записывает все, что вы видите на экране.

person medloh    schedule 11.11.2015