Skip to content

Most visited

Recently visited

navigation
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.Manifest;
20
import android.content.Intent;
21
import android.content.pm.PackageManager;
22
import android.hardware.camera2.CameraAccessException;
23
import android.hardware.camera2.CameraCaptureSession;
24
import android.hardware.camera2.CameraCharacteristics;
25
import android.hardware.camera2.CameraDevice;
26
import android.hardware.camera2.CameraManager;
27
import android.hardware.camera2.CaptureRequest;
28
import android.hardware.camera2.CaptureResult;
29
import android.hardware.camera2.TotalCaptureResult;
30
import android.hardware.camera2.params.StreamConfigurationMap;
31
import android.net.Uri;
32
import android.os.Bundle;
33
import android.os.Handler;
34
import android.os.Looper;
35
import android.provider.Settings;
36
import android.renderscript.RenderScript;
37
import android.support.annotation.NonNull;
38
import android.support.design.widget.Snackbar;
39
import android.support.v4.app.ActivityCompat;
40
import android.support.v7.app.AppCompatActivity;
41
import android.util.Log;
42
import android.util.Size;
43
import android.view.GestureDetector;
44
import android.view.Menu;
45
import android.view.MenuItem;
46
import android.view.MotionEvent;
47
import android.view.Surface;
48
import android.view.SurfaceHolder;
49
import android.view.View;
50
import android.widget.Button;
51
import android.widget.TextView;
52
 
53
import java.util.ArrayList;
54
import java.util.List;
55
 
56
/**
57
 * A small demo of advanced camera functionality with the Android camera2 API.
58
 *
59
 * <p>This demo implements a real-time high-dynamic-range camera viewfinder,
60
 * by alternating the sensor's exposure time between two exposure values on even and odd
61
 * frames, and then compositing together the latest two frames whenever a new frame is
62
 * captured.</p>
63
 *
64
 * <p>The demo has three modes: Regular auto-exposure viewfinder, split-screen manual exposure,
65
 * and the fused HDR viewfinder.  The latter two use manual exposure controlled by the user,
66
 * by swiping up/down on the right and left halves of the viewfinder.  The left half controls
67
 * the exposure time of even frames, and the right half controls the exposure time of odd frames.
68
 * </p>
69
 *
70
 * <p>In split-screen mode, the even frames are shown on the left and the odd frames on the right,
71
 * so the user can see two different exposures of the scene simultaneously.  In fused HDR mode,
72
 * the even/odd frames are merged together into a single image.  By selecting different exposure
73
 * values for the even/odd frames, the fused image has a higher dynamic range than the regular
74
 * viewfinder.</p>
75
 *
76
 * <p>The HDR fusion and the split-screen viewfinder processing is done with RenderScript; as is the
77
 * necessary YUV->RGB conversion. The camera subsystem outputs YUV images naturally, while the GPU
78
 * and display subsystems generally only accept RGB data.  Therefore, after the images are
79
 * fused/composited, a standard YUV->RGB color transform is applied before the the data is written
80
 * to the output Allocation. The HDR fusion algorithm is very simple, and tends to result in
81
 * lower-contrast scenes, but has very few artifacts and can run very fast.</p>
82
 *
83
 * <p>Data is passed between the subsystems (camera, RenderScript, and display) using the
84
 * Android {@link android.view.Surface} class, which allows for zero-copy transport of large
85
 * buffers between processes and subsystems.</p>
86
 */
87
public class HdrViewfinderActivity extends AppCompatActivity implements
88
        SurfaceHolder.Callback, CameraOps.ErrorDisplayer, CameraOps.CameraReadyListener {
89
 
90
    private static final String TAG = "HdrViewfinderDemo";
91
 
92
    private static final String FRAGMENT_DIALOG = "dialog";
93
 
94
    private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
95
 
96
    /**
97
     * View for the camera preview.
98
     */
99
    private FixedAspectSurfaceView mPreviewView;
100
 
101
    /**
102
     * Root view of this activity.
103
     */
104
    private View rootView;
105
 
106
    /**
107
     * This shows the current mode of the app.
108
     */
109
    private TextView mModeText;
110
 
111
    // These show lengths of exposure for even frames, exposure for odd frames, and auto exposure.
112
    private TextView mEvenExposureText, mOddExposureText, mAutoExposureText;
113
 
114
    private Handler mUiHandler;
115
 
116
    private CameraCharacteristics mCameraInfo;
117
 
118
    private Surface mPreviewSurface;
119
    private Surface mProcessingHdrSurface;
120
    private Surface mProcessingNormalSurface;
121
    CaptureRequest.Builder mHdrBuilder;
122
    ArrayList<CaptureRequest> mHdrRequests = new ArrayList<CaptureRequest>(2);
123
 
124
    CaptureRequest mPreviewRequest;
125
 
126
    RenderScript mRS;
127
    ViewfinderProcessor mProcessor;
128
    CameraManager mCameraManager;
129
    CameraOps mCameraOps;
130
 
131
    private int mRenderMode = ViewfinderProcessor.MODE_NORMAL;
132
 
133
    // Durations in nanoseconds
134
    private static final long MICRO_SECOND = 1000;
135
    private static final long MILLI_SECOND = MICRO_SECOND * 1000;
136
    private static final long ONE_SECOND = MILLI_SECOND * 1000;
137
 
138
    private long mOddExposure = ONE_SECOND / 33;
139
    private long mEvenExposure = ONE_SECOND / 33;
140
 
141
    private Object mOddExposureTag = new Object();
142
    private Object mEvenExposureTag = new Object();
143
    private Object mAutoExposureTag = new Object();
144
 
145
    @Override
146
    protected void onCreate(Bundle savedInstanceState) {
147
        super.onCreate(savedInstanceState);
148
        setContentView(R.layout.main);
149
 
150
        rootView = findViewById(R.id.panels);
151
 
152
        mPreviewView = (FixedAspectSurfaceView) findViewById(R.id.preview);
153
        mPreviewView.getHolder().addCallback(this);
154
        mPreviewView.setGestureListener(this, mViewListener);
155
 
156
        Button helpButton = (Button) findViewById(R.id.help_button);
157
        helpButton.setOnClickListener(mHelpButtonListener);
158
 
159
        mModeText = (TextView) findViewById(R.id.mode_label);
160
        mEvenExposureText = (TextView) findViewById(R.id.even_exposure);
161
        mOddExposureText = (TextView) findViewById(R.id.odd_exposure);
162
        mAutoExposureText = (TextView) findViewById(R.id.auto_exposure);
163
 
164
        mUiHandler = new Handler(Looper.getMainLooper());
165
 
166
        mRS = RenderScript.create(this);
167
 
168
        // When permissions are revoked the app is restarted so onCreate is sufficient to check for
169
        // permissions core to the Activity's functionality.
170
        if (!checkCameraPermissions()) {
171
            requestCameraPermissions();
172
        } else {
173
            findAndOpenCamera();
174
        }
175
    }
176
 
177
    @Override
178
    protected void onResume() {
179
        super.onResume();
180
    }
181
 
182
    @Override
183
    protected void onPause() {
184
        super.onPause();
185
 
186
        // Wait until camera is closed to ensure the next application can open it
187
        if (mCameraOps != null) {
188
            mCameraOps.closeCameraAndWait();
189
            mCameraOps = null;
190
        }
191
    }
192
 
193
    @Override
194
    public boolean onCreateOptionsMenu(Menu menu) {
195
        getMenuInflater().inflate(R.menu.main, menu);
196
        return super.onCreateOptionsMenu(menu);
197
    }
198
 
199
    @Override
200
    public boolean onOptionsItemSelected(MenuItem item) {
201
        switch (item.getItemId()) {
202
            case R.id.info: {
203
                MessageDialogFragment.newInstance(R.string.intro_message)
204
                        .show(getFragmentManager(), FRAGMENT_DIALOG);
205
                break;
206
            }
207
        }
208
        return super.onOptionsItemSelected(item);
209
    }
210
 
211
    private GestureDetector.OnGestureListener mViewListener
212
            = new GestureDetector.SimpleOnGestureListener() {
213
 
214
        @Override
215
        public boolean onDown(MotionEvent e) {
216
            return true;
217
        }
218
 
219
        @Override
220
        public boolean onSingleTapUp(MotionEvent e) {
221
            switchRenderMode(1);
222
            return true;
223
        }
224
 
225
        @Override
226
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
227
            if (mRenderMode == ViewfinderProcessor.MODE_NORMAL) return false;
228
 
229
            float xPosition = e1.getAxisValue(MotionEvent.AXIS_X);
230
            float width = mPreviewView.getWidth();
231
            float height = mPreviewView.getHeight();
232
 
233
            float xPosNorm = xPosition / width;
234
            float yDistNorm = distanceY / height;
235
 
236
            final float ACCELERATION_FACTOR = 8;
237
            double scaleFactor = Math.pow(2.f, yDistNorm * ACCELERATION_FACTOR);
238
 
239
            // Even on left, odd on right
240
            if (xPosNorm > 0.5) {
241
                mOddExposure *= scaleFactor;
242
            } else {
243
                mEvenExposure *= scaleFactor;
244
            }
245
 
246
            setHdrBurst();
247
 
248
            return true;
249
        }
250
    };
251
 
252
    /**
253
     * Show help dialogs.
254
     */
255
    private View.OnClickListener mHelpButtonListener = new View.OnClickListener() {
256
        public void onClick(View v) {
257
            MessageDialogFragment.newInstance(R.string.help_text)
258
                    .show(getFragmentManager(), FRAGMENT_DIALOG);
259
        }
260
    };
261
 
262
    /**
263
     * Return the current state of the camera permissions.
264
     */
265
    private boolean checkCameraPermissions() {
266
        int permissionState = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
267
 
268
        // Check if the Camera permission is already available.
269
        if (permissionState != PackageManager.PERMISSION_GRANTED) {
270
            // Camera permission has not been granted.
271
            Log.i(TAG, "CAMERA permission has NOT been granted.");
272
            return false;
273
        } else {
274
            // Camera permissions are available.
275
            Log.i(TAG, "CAMERA permission has already been granted.");
276
            return true;
277
        }
278
    }
279
 
280
    /**
281
     * Attempt to initialize the camera.
282
     */
283
    private void initializeCamera() {
284
        mCameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
285
        if (mCameraManager != null) {
286
            mCameraOps = new CameraOps(mCameraManager,
287
                /*errorDisplayer*/ this,
288
                /*readyListener*/ this,
289
                /*readyHandler*/ mUiHandler);
290
 
291
            mHdrRequests.add(null);
292
            mHdrRequests.add(null);
293
        } else {
294
            Log.e(TAG, "Couldn't initialize the camera");
295
        }
296
    }
297
 
298
    private void requestCameraPermissions() {
299
        boolean shouldProvideRationale =
300
            ActivityCompat.shouldShowRequestPermissionRationale(this,
301
                    Manifest.permission.CAMERA);
302
 
303
        // Provide an additional rationale to the user. This would happen if the user denied the
304
        // request previously, but didn't check the "Don't ask again" checkbox.
305
        if (shouldProvideRationale) {
306
            Log.i(TAG, "Displaying camera permission rationale to provide additional context.");
307
            Snackbar.make(rootView, R.string.camera_permission_rationale, Snackbar
308
                    .LENGTH_INDEFINITE)
309
                    .setAction(R.string.ok, new View.OnClickListener() {
310
                        @Override
311
<