Android异步任务处理实时视频帧

6
我正在使用OpenCV尝试进行实时视频处理。由于处理相当繁重,它会严重延迟输出帧,使得直播流看起来很卡顿。
我想将一些处理工作转移到AsyncTask中。我已经尝试过,并且它确实使视频更加流畅。但是,它最终会同时启动大量任务,然后它们将慢慢开始返回一些结果。
有没有办法减缓这种情况,等待结果,可以使用Synchronize语句或其他方法?
在每个摄像头帧上,我启动其中一个任务。 DoImgProcessing执行长时间的处理并返回字符串结果。
private class LongOperation extends AsyncTask<Mat, Void, String> {

    @Override
    protected String doInBackground(Mat... params) {
        Mat inputFrame = params[0];
        cropToCenter(inputFrame);
        return doImgProcessing(inputFrame);
    }      

    @Override
    protected void onPostExecute(String result) {
        Log.d(TAG, "on post execute: "+result);

    }

    @Override
    protected void onPreExecute() {
        Log.d(TAG, "on pre execute");
    }
}

public Mat onCameraFrame(Mat inputFrame) {
    inputFrame.copyTo(mRgba);//this will be used for the live stream
    LongOperation op = new LongOperation();
    op.execute(inputFrame);
    return mRgba;
}

我会使用一个单线程和阻塞队列来存放帧。 - njzk2
我从未编写过这样的代码,如果您能提供一些示例代码,我很乐意接受您的答案。 - Jameo
2个回答

3
我会这样做:

我会像这样做:

// Example value for a timeout.
private static final long TIMEOUT = 1000L;

private BlockingQueue<Mat> frames = new LinkedBlockingQueue<Mat>();

Thread worker = new Thread() {
    @Override
    public void run() {
        while (running) {
            Mat inputFrame = frames.poll(TIMEOUT, TimeUnit.MILLISECONDS);
            if (inputFrame == null) {
                // timeout. Also, with a try {} catch block poll can be interrupted via Thread.interrupt() so not to wait for the timeout.
                continue;
            }
            cropToCenter(inputFrame);
            String result = doImgProcessing(inputFrame);
        }
    }
};
worker.start();

public Mat onCameraFrame(Mat inputFrame) {
    inputFrame.copyTo(mRgba);//this will be used for the live stream
    frames.put(inputFrame);
    return mRgba;
}

onCameraFrame函数将帧放入队列,工作线程从队列中获取。

这样解耦了帧的接收和处理。您可以使用frames.size()监视队列的增长情况。

这是一个典型的生产者-消费者示例。


很棒的例子。不过有两个问题,首先,这个布尔型的running变量,理论上应该在整个活动中都是运行状态吗?另外,当将帧添加到队列时,您建议我这样说,如果(frames.size() < 4){frames.put(inputFrame);}吗? - Jameo
1
通常,您需要决定停止条件。running = false是一种快速简单的处理方式,但这意味着您将放弃队列的其余部分(这可能是预期行为,也可能不是)。 - njzk2
1
关于队列大小,您可以使用固定容量队列(在构造函数中添加限制),并使用offer(inputFrame),它将尝试放置对象,并在没有可用空间时返回false。这意味着您可能会丢失帧,但您将获得接近实时处理的结果。 - njzk2

1
如果您在每个帧上都这样做,那么似乎您需要一个线程。AsyncTask用于在另一个线程上执行一次性活动。在这里,您想要重复执行它。只需创建一个线程,当它完成一帧时,让它向处理程序发送消息以在UI线程上运行后续步骤。它可以在其循环的顶部等待信号量准备好下一帧。

我可以看出那可能行得通。但AsyncTask不就是一个线程吗? - Jameo
你认为让系统同时启动多个帧处理有优势吗? - Jameo
它在一个线程上运行,但该线程会退出。如果您一直在做某些事情,那实际上与您想要的相反。至于多个异步任务-实际上同时获取多个异步任务很困难,这需要在某些Android版本上编写特殊代码。而且只有在操作系统足够聪明以在不同核心上运行它们时才有用,我对此表示怀疑。 - Gabe Sechan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接