Compare commits

..
2 Commits
10 changed files with 4 additions and 707 deletions
View File
-9
View File
@@ -36,13 +36,4 @@ dependencies {
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
// Required for CameraX
def camerax_version = "1.2.3"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.camera:camera-video:${camerax_version}"
implementation "androidx.camera:camera-view:${camerax_version}"
implementation "androidx.camera:camera-extensions:${camerax_version}"
}
-5
View File
@@ -2,11 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.camera"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
@@ -1,76 +0,0 @@
package com.example.ueberwachungssystem;
import android.content.Context;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.util.Log;
import android.view.SurfaceView;
import android.widget.VideoView;
import java.io.File;
import java.io.IOException;
public class AudioRecorder {
private final Context context;
private Camera camera;
private MediaRecorder mediaRecorder = null;
private SurfaceView surfaceView;
public AudioRecorder (Context context) {
this.context = context;
this.surfaceView = new SurfaceView(context);
}
public void startRecording() {
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mediaRecorder.setOutputFile(context.getFilesDir() + "/audio.mp3");
try {
mediaRecorder.prepare();
mediaRecorder.start();
} catch (IOException e) {
Log.e("audioRecorder", "prepare() failed");
}
}
public void stopRecording() {
if (mediaRecorder != null) {
try {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
} catch (RuntimeException e) {
// RuntimeException may be thrown if the MediaRecorder is in an invalid state
e.printStackTrace();
}
}
}
public void playAudio() {
MediaPlayer mp = new MediaPlayer();
try {
mp.setDataSource(context.getFilesDir() + "/audio.mp3");
mp.prepare();
mp.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public void deleteRecording(){
File file = new File(context.getFilesDir() + "/audio.mp3");
if (file.exists()) {
file.delete();
}
}
}
@@ -1,36 +0,0 @@
package com.example.ueberwachungssystem.Detection;
import android.util.Log;
import java.util.Calendar;
/** Detection Report Class */
public class DetectionReport {
public String timeStamp;
public String detectionType;
public float detectedValue;
public String detectorID;
public DetectionReport(String detectorID, String detectionType, float detectedAmplitude) {
this.timeStamp = String.valueOf(Calendar.getInstance().getTime());
this.detectionType = detectionType;
this.detectedValue = detectedAmplitude;
this.detectorID = detectorID;
}
/** Get Detection Report in String format */
public String toString() {
String time = "Time: " + "[" + this.timeStamp + "]";
String type = "Type: " + "[" + this.detectionType + "]";
String value = "Value: " + "[" + this.detectedValue + "]";
String id = "ID: " + "[" + this.detectorID + "]";
return String.join("\t", time, type, value, id);
}
/** Debug Report */
public void log(String tag) {
Log.d(tag, this.toString());
}
}
@@ -1,38 +0,0 @@
package com.example.ueberwachungssystem.Detection;
import androidx.annotation.NonNull;
abstract public class Detector {
private OnDetectionListener listener;
/** Constructor - takes context of current activity */
public Detector() {}
/** On Detection Listener - runs when violation is reported */
public interface OnDetectionListener {
void onDetection(@NonNull DetectionReport detectionReport);
}
public void setOnDetectionListener(@NonNull OnDetectionListener listener) {
this.listener = listener;
}
/** Triggers onDetectionListener - call this to trigger violation/alarm */
public void reportViolation(String detectorID, String detectionType, float amplitude) {
if (listener != null) {
DetectionReport detectionReport = new DetectionReport(detectorID, detectionType, amplitude);
listener.onDetection(detectionReport);
} else {
throw new IllegalStateException("No listener set for violation reporting");
}
}
/** Starts Detection (abstract method: needs to be overridden in child class) */
public abstract void startDetection();
/** Stops Detection (abstract method: needs to be overridden in child class) */
public abstract void stopDetection();
}
@@ -1,257 +0,0 @@
package com.example.ueberwachungssystem.Detection;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.media.Image;
import android.util.Size;
import android.view.Surface;
import android.widget.Toast;
import android.widget.VideoView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ExperimentalGetImage;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview;
import androidx.camera.core.VideoCapture;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutionException;
/**
* Video Detector inherits some methods from abstract Detector class (more info there)
*
*
* USE FROM MAIN ACTIVITY:
*
* VideoDetector vd = new VideoDetector(this);
* vd.setPreview(previewView); //THIS IS OPTIONAL!
* vd.setOnDetectionListener(new Detector.OnDetectionListener{...});
* vd.startDetection();
* vd.stopDetection
*
* */
@ExperimentalGetImage
public class VideoDetector extends Detector {
// Calling Activity
private final Context context;
// Camera Provider
private ProcessCameraProvider cameraProvider;
private Boolean isDetectionRunning = false;
// Preview Camera Image
private PreviewView previewView = null;
private VideoCapture videoCapture = null;
// Detect Violation
private static final float PIXEL_THRESHOLD = 30f; // Luminosity (brightness channel of YUV_420_888)
private static final float ALARM_THRESHOLD = 0.2f; // Percent of pixels changed
private ByteBuffer previousBuffer = null;
/**
* Constructor
* @param context: the context of calling activity (usually "this")
* */
public VideoDetector(Context context) {
super();
this.context = context;
}
/**
* Starts the Video Detection
* */
@Override
public void startDetection() {
if (isDetectionRunning)
return;
// Request Camera Provider
final ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(context);
//Check for Camera availability
cameraProviderFuture.addListener(() -> {
try {
cameraProvider = cameraProviderFuture.get();
bindAnalysis(cameraProvider);
isDetectionRunning = true;
previousBuffer = null;
} catch (ExecutionException | InterruptedException e) {
// No errors need to be handled for this Future. This should never be reached.
}
}, ContextCompat.getMainExecutor(context));
}
/**
* Stops the Video Detection
* */
@Override
public void stopDetection() {
if (!isDetectionRunning)
return;
cameraProvider.unbindAll();
isDetectionRunning = false;
}
/**
* Set PreviewView to show video feed while detecting
* this is optional and does not need to be called
* */
public void setPreviewView(PreviewView previewView) {
this.previewView = previewView;
}
/**
* Binds the Luminosity Analyzer (configure and run Analysis)
* @param cameraProvider: CameraProvider of Context passed by Constructor
* */
@SuppressLint("RestrictedApi")
private void bindAnalysis(@NonNull ProcessCameraProvider cameraProvider) {
// Configure and create Image Analysis
ImageAnalysis.Builder builder = new ImageAnalysis.Builder();
builder.setTargetResolution(new Size(640, 480));
builder.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST);
builder.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888);
ImageAnalysis imageAnalysis = builder.build();
// Set Analyzer
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(context), imageProxy -> {
if (imageProxy.getFormat() == ImageFormat.YUV_420_888) {
Image image = imageProxy.getImage();
assert image != null;
// Changed Pixel Detection
int pixelChanged = getChangedPixelCount(image);
int width = image.getWidth();
int height = image.getHeight();
float percentPixelChanged = (float) 100f * pixelChanged / (width * height);
if (percentPixelChanged > ALARM_THRESHOLD)
reportViolation("0", "Video", percentPixelChanged);
}
imageProxy.close();
});
// Create Preview
Preview preview = new Preview.Builder().build();
// Specify which Camera to use
CameraSelector cameraSelector = new CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build();
// Update PreviewView if set
if (previewView != null)
preview.setSurfaceProvider(previewView.getSurfaceProvider());
videoCapture = new VideoCapture.Builder()
.setVideoFrameRate(30)
.setTargetRotation(Surface.ROTATION_0)
.build();
cameraProvider.bindToLifecycle((LifecycleOwner) context, cameraSelector, imageAnalysis, videoCapture);
}
@SuppressLint("RestrictedApi")
public void startRecording() {
File dir = context.getFilesDir();
String filename = "recording";
String vidFilePath = dir.getAbsolutePath() + "/" + filename + ".mp4";
File vidFile = new File(vidFilePath);
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
videoCapture.startRecording(
new VideoCapture.OutputFileOptions.Builder(vidFile).build(),
context.getMainExecutor(),
new VideoCapture.OnVideoSavedCallback() {
@Override
public void onVideoSaved(@NonNull VideoCapture.OutputFileResults outputFileResults) {
Toast.makeText(context, "recording saved", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
Toast.makeText(context, "recording failed", Toast.LENGTH_SHORT).show();
}
}
);
}
@SuppressLint("RestrictedApi")
public void stopRecording(){
videoCapture.stopRecording();
}
/**
* Calculate Amount of Pixels changed using a threshold
* @param image
* */
private int getChangedPixelCount(Image image) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
if (previousBuffer == null) {
previousBuffer = ByteBuffer.allocate(buffer.capacity());
buffer.rewind();
previousBuffer.put(buffer);
previousBuffer.rewind();
return 0;
}
int width = image.getWidth();
int height = image.getHeight();
int yRowStride = image.getPlanes()[0].getRowStride();
int yPixelStride = image.getPlanes()[0].getPixelStride();
int changedPixelCount = 0;
// Count changed pixels
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; x++) {
int index = (y * yRowStride) + (x * yPixelStride);
int luminosity = (buffer.get(index) & 0xff);
int previousLuminosity = (previousBuffer.get(index) & 0xff);
int diff = Math.abs(luminosity - previousLuminosity);
if (diff > PIXEL_THRESHOLD)
changedPixelCount++;
}
}
// Reset and copy Byte Buffer
buffer.rewind();
previousBuffer.rewind();
previousBuffer.put(buffer);
return changedPixelCount;
}
public void playVideo(VideoView videoView) {
videoView.setVideoPath(context.getFilesDir() + "/recording.mp4");
videoView.start();
}
}
@@ -1,147 +0,0 @@
package com.example.ueberwachungssystem;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.view.Surface;
import android.widget.Toast;
import android.widget.VideoView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.Preview;
import androidx.camera.core.VideoCapture;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.util.concurrent.ExecutionException;
public class DetectionRecorder {
private final Context context;
private ProcessCameraProvider cameraProvider;
private VideoCapture videoCapture = null;
private PreviewView previewView = null;
private boolean isRunning = false;
private boolean isVideoPlaying = false;
// Constructor
public DetectionRecorder(Context context) {
this.context = context;
}
public void start() {
if (isRunning)
return;
isRunning = true;
// Request Camera Provider
final ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(context);
//Check for Camera availability
cameraProviderFuture.addListener(() -> {
try {
cameraProvider = cameraProviderFuture.get();
startCameraX(cameraProvider);
} catch (ExecutionException | InterruptedException e) {
// No errors need to be handled for this Future. This should never be reached.
}
}, ContextCompat.getMainExecutor(context));
}
@SuppressLint("RestrictedApi")
public void stop() {
if (!isRunning)
return;
videoCapture.stopRecording();
cameraProvider.unbind(videoCapture);
isRunning = false;
}
@SuppressLint("RestrictedApi")
private void startCameraX(ProcessCameraProvider cameraProvider) {
//cameraProvider.unbindAll();
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
// Preview Use Case
Preview preview = new Preview.Builder().build();
if (previewView != null)
preview.setSurfaceProvider(previewView.getSurfaceProvider());
// Video Capture Use Case
videoCapture = new VideoCapture.Builder()
.setVideoFrameRate(30)
.setTargetRotation(Surface.ROTATION_0)
.build();
cameraProvider.bindToLifecycle((LifecycleOwner) context, cameraSelector, preview, videoCapture);
File dir = context.getFilesDir();
String filename = "recording";
String vidFilePath = dir.getAbsolutePath() + "/" + filename + ".mp4";
File vidFile = new File(vidFilePath);
if (ActivityCompat.checkSelfPermission(context, android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
videoCapture.startRecording(
new VideoCapture.OutputFileOptions.Builder(vidFile).build(),
context.getMainExecutor(),
new VideoCapture.OnVideoSavedCallback() {
@Override
public void onVideoSaved(@NonNull VideoCapture.OutputFileResults outputFileResults) {
Toast.makeText(context, "recording saved", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
Toast.makeText(context, "recording failed", Toast.LENGTH_SHORT).show();
}
}
);
}
/** Set PreviewView to show Image */
public void setPreviewView(PreviewView previewView) {
this.previewView = previewView;
}
private void playAudio() {
MediaPlayer mp = new MediaPlayer();
try {
mp.setDataSource(context.getFilesDir() + "/audio.gpp");
mp.prepare();
mp.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public void playVideo(VideoView videoView) {
if (isVideoPlaying)
return;
videoView.setVideoPath(context.getFilesDir() + "/recording.mp4");
videoView.start();
}
}
@@ -1,120 +1,15 @@
package com.example.ueberwachungssystem;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.camera.core.ExperimentalGetImage;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import android.widget.VideoView;
import com.example.ueberwachungssystem.Detection.DetectionReport;
import com.example.ueberwachungssystem.Detection.Detector;
import com.example.ueberwachungssystem.Detection.VideoDetector;
import java.io.File;
import java.util.Arrays;
@ExperimentalGetImage
public class MainActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CODE = 111;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.textView);
VideoView videoView = findViewById(R.id.videoView);
PreviewView previewView = findViewById(R.id.previewView);
File directory = getFilesDir();
AudioRecorder ar = new AudioRecorder(this);
//DetectionRecorder detectionRecorder = new DetectionRecorder(this);
//detectionRecorder.setPreviewView(previewView);
ToggleButton toggleButton = findViewById(R.id.previewButton);
ToggleButton toggleButton2 = findViewById(R.id.toggleButton2);
toggleButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getRecordVideoAccess();
File[] files = directory.listFiles();
textView.setText(Arrays.toString(files));
if (isRecordVideoAllowed() && toggleButton.isChecked())
{
ar.startRecording();
//vd.startRecording();
//vd.playVideo(videoView);
//detectionRecorder.playVideo(videoView);
//detectionRecorder.start();
}
else {
ar.stopRecording();
//vd.stopRecording();
//detectionRecorder.stop();
}
}
});
toggleButton2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (toggleButton.isChecked())
{
ar.playAudio();
}
else {
ar.deleteRecording();
}
}
});
}
private boolean isRecordVideoAllowed() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED;
}
private void getRecordVideoAccess() {
if (!isRecordVideoAllowed()) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, PERMISSION_REQUEST_CODE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE && grantResults.length > 0) {
boolean cameraRights = grantResults[0] == PackageManager.PERMISSION_GRANTED;
boolean recordAudioRights = grantResults[1] == PackageManager.PERMISSION_GRANTED;
if (cameraRights && recordAudioRights) {
Toast.makeText(this, "permissions granted", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "permissions denied", Toast.LENGTH_LONG).show();
}
}
//
}
}
+3 -33
View File
@@ -1,48 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textSize="20dp"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ToggleButton
android:id="@+id/previewButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ToggleButton" />
<ToggleButton
android:id="@+id/toggleButton2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ToggleButton" />
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="200dp" />
<VideoView
android:id="@+id/videoView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>