این وبسایت یک نسخه آزمایشی از سایت developer android می‌باشد که در حال ترجمه می‌باشد.
برای پیوستن به تیم ترجمه و خواندن مستندات اینجا کلیک کنید.
HdrViewfinder / src / com.example.android.hdrviewfinder /

HdrViewfinderActivity.java

1
/*
2
 * Copyright (C) 2014 The Android Open Source Project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
17
package com.example.android.hdrviewfinder;
18
 
19
import android.app.Activity;
20
import android.hardware.camera2.CameraAccessException;
21
import android.hardware.camera2.CameraCaptureSession;
22
import android.hardware.camera2.CameraCharacteristics;
23
import android.hardware.camera2.CameraDevice;
24
import android.hardware.camera2.CameraManager;
25
import android.hardware.camera2.CaptureRequest;
26
import android.hardware.camera2.CaptureResult;
27
import android.hardware.camera2.TotalCaptureResult;
28
import android.hardware.camera2.params.StreamConfigurationMap;
29
import android.os.Bundle;
30
import android.os.Handler;
31
import android.os.Looper;
32
import android.renderscript.RenderScript;
33
import android.util.Log;
34
import android.util.Size;
35
import android.view.GestureDetector;
36
import android.view.Menu;
37
import android.view.MenuItem;
38
import android.view.MotionEvent;
39
import android.view.Surface;
40
import android.view.SurfaceHolder;
41
import android.view.View;
42
import android.widget.Button;
43
import android.widget.TextView;
44
 
45
import java.util.ArrayList;
46
import java.util.List;
47
 
48
/**
49
 * A small demo of advanced camera functionality with the Android camera2 API.
50
 *
51
 * <p>This demo implements a real-time high-dynamic-range camera viewfinder,
52
 * by alternating the sensor's exposure time between two exposure values on even and odd
53
 * frames, and then compositing together the latest two frames whenever a new frame is
54
 * captured.</p>
55
 *
56
 * <p>The demo has three modes: Regular auto-exposure viewfinder, split-screen manual exposure,
57
 * and the fused HDR viewfinder.  The latter two use manual exposure controlled by the user,
58
 * by swiping up/down on the right and left halves of the viewfinder.  The left half controls
59
 * the exposure time of even frames, and the right half controls the exposure time of odd frames.
60
 * </p>
61
 *
62
 * <p>In split-screen mode, the even frames are shown on the left and the odd frames on the right,
63
 * so the user can see two different exposures of the scene simultaneously.  In fused HDR mode,
64
 * the even/odd frames are merged together into a single image.  By selecting different exposure
65
 * values for the even/odd frames, the fused image has a higher dynamic range than the regular
66
 * viewfinder.</p>
67
 *
68
 * <p>The HDR fusion and the split-screen viewfinder processing is done with RenderScript; as is the
69
 * necessary YUV->RGB conversion. The camera subsystem outputs YUV images naturally, while the GPU
70
 * and display subsystems generally only accept RGB data.  Therefore, after the images are
71
 * fused/composited, a standard YUV->RGB color transform is applied before the the data is written
72
 * to the output Allocation. The HDR fusion algorithm is very simple, and tends to result in
73
 * lower-contrast scenes, but has very few artifacts and can run very fast.</p>
74
 *
75
 * <p>Data is passed between the subsystems (camera, RenderScript, and display) using the
76
 * Android {@link android.view.Surface} class, which allows for zero-copy transport of large
77
 * buffers between processes and subsystems.</p>
78
 */
79
public class HdrViewfinderActivity extends Activity implements
80
        SurfaceHolder.Callback, CameraOps.ErrorDisplayer, CameraOps.CameraReadyListener {
81
 
82
    private static final String TAG = "HdrViewfinderDemo";
83
 
84
    private static final String FRAGMENT_DIALOG = "dialog";
85
 
86
    /**
87
     * View for the camera preview.
88
     */
89
    private FixedAspectSurfaceView mPreviewView;
90
 
91
    /**
92
     * This shows the current mode of the app.
93
     */
94
    private TextView mModeText;
95
 
96
    // These show lengths of exposure for even frames, exposure for odd frames, and auto exposure.
97
    private TextView mEvenExposureText, mOddExposureText, mAutoExposureText;
98
 
99
    private Handler mUiHandler;
100
 
101
    private CameraCharacteristics mCameraInfo;
102
 
103
    private Surface mPreviewSurface;
104
    private Surface mProcessingHdrSurface;
105
    private Surface mProcessingNormalSurface;
106
    CaptureRequest.Builder mHdrBuilder;
107
    ArrayList<CaptureRequest> mHdrRequests = new ArrayList<CaptureRequest>(2);
108
 
109
    CaptureRequest mPreviewRequest;
110
 
111
    RenderScript mRS;
112
    ViewfinderProcessor mProcessor;
113
    CameraManager mCameraManager;
114
    CameraOps mCameraOps;
115
 
116
    private int mRenderMode = ViewfinderProcessor.MODE_NORMAL;
117
 
118
    // Durations in nanoseconds
119
    private static final long MICRO_SECOND = 1000;
120
    private static final long MILLI_SECOND = MICRO_SECOND * 1000;
121
    private static final long ONE_SECOND = MILLI_SECOND * 1000;
122
 
123
    private long mOddExposure = ONE_SECOND / 33;
124
    private long mEvenExposure = ONE_SECOND / 33;
125
 
126
    private Object mOddExposureTag = new Object();
127
    private Object mEvenExposureTag = new Object();
128
    private Object mAutoExposureTag = new Object();
129
 
130
    @Override
131
    protected void onCreate(Bundle savedInstanceState) {
132
        super.onCreate(savedInstanceState);
133
        setContentView(R.layout.main);
134
 
135
        mPreviewView = (FixedAspectSurfaceView) findViewById(R.id.preview);
136
        mPreviewView.getHolder().addCallback(this);
137
        mPreviewView.setGestureListener(this, mViewListener);
138
 
139
        Button helpButton = (Button) findViewById(R.id.help_button);
140
        helpButton.setOnClickListener(mHelpButtonListener);
141
 
142
        mModeText = (TextView) findViewById(R.id.mode_label);
143
        mEvenExposureText = (TextView) findViewById(R.id.even_exposure);
144
        mOddExposureText = (TextView) findViewById(R.id.odd_exposure);
145
        mAutoExposureText = (TextView) findViewById(R.id.auto_exposure);
146
 
147
        mUiHandler = new Handler(Looper.getMainLooper());
148
 
149
        mCameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
150
        mCameraOps = new CameraOps(mCameraManager,
151
                /*errorDisplayer*/ this,
152
                /*readyListener*/ this,
153
                /*readyHandler*/ mUiHandler);
154
 
155
        mHdrRequests.add(null);
156
        mHdrRequests.add(null);
157
 
158
        mRS = RenderScript.create(this);
159
    }
160
 
161
    @Override
162
    protected void onResume() {
163
        super.onResume();
164
 
165
        findAndOpenCamera();
166
    }
167
 
168
    @Override
169
    protected void onPause() {
170
        super.onPause();
171
 
172
        // Wait until camera is closed to ensure the next application can open it
173
        mCameraOps.closeCameraAndWait();
174
    }
175
 
176
    @Override
177
    public boolean onCreateOptionsMenu(Menu menu) {
178
        getMenuInflater().inflate(R.menu.main, menu);
179
        return super.onCreateOptionsMenu(menu);
180
    }
181
 
182
    @Override
183
    public boolean onOptionsItemSelected(MenuItem item) {
184
        switch (item.getItemId()) {
185
            case R.id.info: {
186
                MessageDialogFragment.newInstance(R.string.intro_message)
187
                        .show(getFragmentManager(), FRAGMENT_DIALOG);
188
                break;
189
            }
190
        }
191
        return super.onOptionsItemSelected(item);
192
    }
193
 
194
    private GestureDetector.OnGestureListener mViewListener
195
            = new GestureDetector.SimpleOnGestureListener() {
196
 
197
        @Override
198
        public boolean onDown(MotionEvent e) {
199
            return true;
200
        }
201
 
202
        @Override
203
        public boolean onSingleTapUp(MotionEvent e) {
204
            switchRenderMode(1);
205
            return true;
206
        }
207
 
208
        @Override
209
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
210
            if (mRenderMode == ViewfinderProcessor.MODE_NORMAL) return false;
211
 
212
            float xPosition = e1.getAxisValue(MotionEvent.AXIS_X);
213
            float width = mPreviewView.getWidth();
214
            float height = mPreviewView.getHeight();
215
 
216
            float xPosNorm = xPosition / width;
217
            float yDistNorm = distanceY / height;
218
 
219
            final float ACCELERATION_FACTOR = 8;
220
            double scaleFactor = Math.pow(2.f, yDistNorm * ACCELERATION_FACTOR);
221
 
222
            // Even on left, odd on right
223
            if (xPosNorm > 0.5) {
224
                mOddExposure *= scaleFactor;
225
            } else {
226
                mEvenExposure *= scaleFactor;
227
            }
228
 
229
            setHdrBurst();
230
 
231
            return true;
232
        }
233
    };
234
 
235
    // Show help dialog
236
    private View.OnClickListener mHelpButtonListener = new View.OnClickListener() {
237
        public void onClick(View v) {
238
            MessageDialogFragment.newInstance(R.string.help_text)
239
                    .show(getFragmentManager(), FRAGMENT_DIALOG);
240
        }
241
    };
242
 
243
    private void findAndOpenCamera() {
244
 
245
        String errorMessage = "Unknown error";
246
        boolean foundCamera = false;
247
        try {
248
            // Find first back-facing camera that has necessary capability
249
            String[] cameraIds = mCameraManager.getCameraIdList();
250
            for (String id : cameraIds) {
251
                CameraCharacteristics info = mCameraManager.getCameraCharacteristics(id);
252
                int facing = info.get(CameraCharacteristics.LENS_FACING);
253
 
254
                int level = info.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
255
                boolean hasFullLevel
256
                        = (level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
257
 
258
                int[] capabilities = info.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
259
                int syncLatency = info.get(CameraCharacteristics.SYNC_MAX_LATENCY);
260
                boolean hasManualControl = hasCapability(capabilities,
261
                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR);
262
                boolean hasEnoughCapability = hasManualControl &&
263
                        syncLatency == CameraCharacteristics.SYNC_MAX_LATENCY_PER_FRAME_CONTROL;
264
 
265
                // All these are guaranteed by
266
                // CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, but checking for only
267
                // the things we care about expands range of devices we can run on
268
                // We want:
269
                //  - Back-facing camera
270
                //  - Manual sensor control
271
                //  - Per-frame synchronization (so that exposure can be changed every frame)
272
                if (facing == CameraCharacteristics.LENS_FACING_BACK &&
273
                        (hasFullLevel || hasEnoughCapability)) {
274
                    // Found suitable camera - get info, open, and set up outputs
275
                    mCameraInfo = info;
276
                    mCameraOps.openCamera(id);
277
                    configureSurfaces();
278
                    foundCamera = true;
279
                    break;
280
                }
281
            }
282
            if (!foundCamera) {
283
                errorMessage = getString(R.string.camera_no_good);
284
            }
285
        } catch (CameraAccessException e) {
286
            errorMessage = getErrorString(e);
287
        }
288
 
289
        if (!foundCamera) {
290
            showErrorDialog(errorMessage);
291
        }
292
    }
293
 
294
    private boolean hasCapability(int[] capabilities, int capability) {
295
        for (int c : capabilities) {
296
            if (c == capability) return true;
297
        }
298
        return false;
299
    }
300
 
301
    private void switchRenderMode(int direction) {
302
        mRenderMode = (mRenderMode + direction) % 3;
303
 
304
        mModeText.setText(getResources().getStringArray(R.array.mode_label_array)[mRenderMode]);
305
 
306
        if (mProcessor != null) {
307
            mProcessor.setRenderMode(mRenderMode);
308
        }
309
        if (mRenderMode == ViewfinderProcessor.MODE_NORMAL) {
310
            mCameraOps.setRepeatingRequest(mPreviewRequest,
311
                    mCaptureCallback, mUiHandler);
312
        } else {
313
            setHdrBurst();
314
        }
315
    }
316
 
317
    /**
318
     * Configure the surfaceview and RS processing
319
     */
320
    private void configureSurfaces() {
321
        // Find a good size for output - largest 16:9 aspect ratio that's less than 720p
322
        final int MAX_WIDTH = 1280;
323
        final float TARGET_ASPECT = 16.f / 9.f;
324
        final float ASPECT_TOLERANCE = 0.1f;
325
 
326
        StreamConfigurationMap configs =
327
                mCameraInfo.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
328
 
329
        Size[] outputSizes = configs.getOutputSizes(SurfaceHolder.class);
330
 
331
        Size outputSize = outputSizes[0];
332
        float outputAspect = (float) outputSize.getWidth() / outputSize.getHeight();
333
        for (Size candidateSize : outputSizes) {
334
            if (candidateSize.getWidth() > MAX_WIDTH) continue;
335
            float candidateAspect = (float) candidateSize.getWidth() / candidateSize.getHeight();
336
            boolean goodCandidateAspect =
337
                    Math.abs(candidateAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
338
            boolean goodOutputAspect =
339
                    Math.abs(outputAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
340
            if ((goodCandidateAspect && !goodOutputAspect) ||
341
                    candidateSize.getWidth() > outputSize.getWidth()) {
342
                outputSize = candidateSize;
343
                outputAspect = candidateAspect;
344
            }
345
        }
346
        Log.i(TAG, "Resolution chosen: " + outputSize);
347
 
348
        // Configure processing
349
        mProcessor = new ViewfinderProcessor(mRS, outputSize);
350
        setupProcessor();
351
 
352
        // Configure the output view - this will fire surfaceChanged
353
        mPreviewView.setAspectRatio(outputAspect);
354
        mPreviewView.getHolder().setFixedSize(outputSize.getWidth(), outputSize.getHeight());
355
    }
356
 
357
    /**
358
     * Once camera is open and output surfaces are ready, configure the RS processing
359
     * and the camera device inputs/outputs.
360
     */
361
    private void setupProcessor() {
362
        if (mProcessor == null || mPreviewSurface == null) return;
363
 
364
        mProcessor.setOutputSurface(mPreviewSurface);
365
        mProcessingHdrSurface = mProcessor.getInputHdrSurface();
366
        mProcessingNormalSurface = mProcessor.getInputNormalSurface();
367
 
368
        List<Surface> cameraOutputSurfaces = new ArrayList<Surface>();
369
        cameraOutputSurfaces.add(mProcessingHdrSurface);
370
        cameraOutputSurfaces.add(mProcessingNormalSurface);
371
 
372
        mCameraOps.setSurfaces(cameraOutputSurfaces);
373
    }
374
 
375
    /**
376
     * Start running an HDR burst on a configured camera session
377
     */
378
    public void setHdrBurst() {
379
 
380
        mHdrBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, 1600);
381
        mHdrBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, ONE_SECOND / 30);
382
 
383
        mHdrBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, mEvenExposure);
384
        mHdrBuilder.setTag(mEvenExposureTag);
385
        mHdrRequests.set(0, mHdrBuilder.build());
386
 
387
        mHdrBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, mOddExposure);
388
        mHdrBuilder.setTag(mOddExposureTag);
389
        mHdrRequests.set(1, mHdrBuilder.build());
390
 
391
        mCameraOps.setRepeatingBurst(mHdrRequests, mCaptureCallback, mUiHandler);
392
    }
393
 
394
    /**
395
     * Listener for completed captures
396
     * Invoked on UI thread
397
     */
398
    private CameraCaptureSession.CaptureCallback mCaptureCallback
399
            = new CameraCaptureSession.CaptureCallback() {
400
 
401
        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
402
                                       TotalCaptureResult result) {
403
 
404
            // Only update UI every so many frames
405
            // Use an odd number here to ensure both even and odd exposures get an occasional update
406
            long frameNumber = result.getFrameNumber();
407
            if (frameNumber % 3 != 0) return;
408
 
409
            long exposureTime = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
410
 
411
            // Format exposure time nicely
412
            String exposureText;
413
            if (exposureTime > ONE_SECOND) {
414
                exposureText = String.format("%.2f s", exposureTime / 1e9);
415
            } else if (exposureTime > MILLI_SECOND) {
416
                exposureText = String.format("%.2f ms", exposureTime / 1e6);
417
            } else if (exposureTime > MICRO_SECOND) {
418
                exposureText = String.format("%.2f us", exposureTime / 1e3);
419
            } else {
420
                exposureText = String.format("%d ns", exposureTime);
421
            }
422
 
423
            Object tag = request.getTag();
424
            Log.i(TAG, "Exposure: " + exposureText);
425
 
426
            if (tag == mEvenExposureTag) {
427
                mEvenExposureText.setText(exposureText);
428
 
429
                mEvenExposureText.setEnabled(true);
430
                mOddExposureText.setEnabled(true);
431
                mAutoExposureText.setEnabled(false);
432
            } else if (tag == mOddExposureTag) {
433
                mOddExposureText.setText(exposureText);
434
 
435
                mEvenExposureText.setEnabled(true);
436
                mOddExposureText.setEnabled(true);
437
                mAutoExposureText.setEnabled(false);
438
            } else {
439
                mAutoExposureText.setText(exposureText);
440
 
441
                mEvenExposureText.setEnabled(false);
442
                mOddExposureText.setEnabled(false);
443
                mAutoExposureText.setEnabled(true);
444
            }
445
        }
446
    };
447
 
448
    /**
449
     * Callbacks for the FixedAspectSurfaceView
450
     */
451
 
452
    @Override
453
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
454
        mPreviewSurface = holder.getSurface();
455
 
456
        setupProcessor();
457
    }
458
 
459
    @Override
460
    public void surfaceCreated(SurfaceHolder holder) {
461
        // ignored
462
    }
463
 
464
    @Override
465
    public void surfaceDestroyed(SurfaceHolder holder) {
466
        mPreviewSurface = null;
467
    }
468
 
469
    /**
470
     * Callbacks for CameraOps
471
     */
472
    @Override
473
    public void onCameraReady() {
474
        // Ready to send requests in, so set them up
475
        try {
476
            CaptureRequest.Builder previewBuilder =
477
                    mCameraOps.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
478
            previewBuilder.addTarget(mProcessingNormalSurface);
479
            previewBuilder.setTag(mAutoExposureTag);
480
            mPreviewRequest = previewBuilder.build();
481
 
482
            mHdrBuilder =
483
                    mCameraOps.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
484
            mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE,
485
                    CaptureRequest.CONTROL_AE_MODE_OFF);
486
            mHdrBuilder.addTarget(mProcessingHdrSurface);
487
 
488
            switchRenderMode(0);
489
 
490
        } catch (CameraAccessException e) {
491
            String errorMessage = getErrorString(e);
492
            showErrorDialog(errorMessage);
493
        }
494
    }
495
 
496
    /**
497
     * Utility methods
498
     */
499
    @Override
500
    public void showErrorDialog(String errorMessage) {
501
        MessageDialogFragment.newInstance(errorMessage).show(getFragmentManager(), FRAGMENT_DIALOG);
502
    }
503
 
504
    @Override
505
    public String getErrorString(CameraAccessException e) {
506
        String errorMessage;
507
        switch (e.getReason()) {
508
            case CameraAccessException.CAMERA_DISABLED:
509
                errorMessage = getString(R.string.camera_disabled);
510
                break;
511
            case CameraAccessException.CAMERA_DISCONNECTED:
512
                errorMessage = getString(R.string.camera_disconnected);
513
                break;
514
            case CameraAccessException.CAMERA_ERROR:
515
                errorMessage = getString(R.string.camera_error);
516
                break;
517
            default:
518