Reputation: 423
I have created a custom launcher screen application that is displayed within the device applications. Within the application, I have included a screen recording feature using the media projection API. Everything was working perfectly, but whenever I initiate the screen recording, it goes back to the home screen. I want the screen recording to overlay the currently opened application instead of going back to the home screen. Can anyone help me solve this issue?
here is my code:-
//ScreenRecordingService.java
package com.Services;
import static android.app.Activity.RESULT_OK;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.mdmtesting.hiveliveviewer.R;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScreenRecordingService extends Service {
private static final String VIDEO_MIME_TYPE = "video/avc";
private static final int VIDEO_WIDTH = 1280;
private static final int VIDEO_HEIGHT = 720;
private static final int VIDEO_BIT_RATE = 5000000;
private static final int VIDEO_FRAME_RATE = 30;
private static final int VIDEO_I_FRAME_INTERVAL = 1;
private static final String TAG = "ScreenRecordingService";
private static final String CHANNEL_ID = "screen_recording_channel";
private MediaProjectionManager mediaProjectionManager;
private MediaProjection mediaProjection;
private MediaCodec mEncoder;
private Surface mInputSurface;
private MediaCodec.BufferInfo mBufferInfo;
private ScheduledExecutorService scheduler;
private WebSocketService webSocketService;
private boolean isBound = false;
@Override
public void onCreate() {
super.onCreate();
// Create notification channel for Android O and above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"Screen Recording Service",
NotificationManager.IMPORTANCE_LOW
);
NotificationManager manager = getSystemService(NotificationManager.class);
if (manager != null) {
manager.createNotificationChannel(channel);
}
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service started");
// Start the service as a foreground service
startForegroundService();
Intent webSocketIntent = new Intent(this, WebSocketService.class);
bindService(webSocketIntent, connection, Context.BIND_AUTO_CREATE);
if (intent != null && intent.hasExtra("resultData")) {
Intent resultData = intent.getParcelableExtra("resultData");
if (resultData != null) {
startScreenRecording(resultData);
} else {
Log.e(TAG, "No resultData in Intent");
stopSelf();
}
} else {
Log.e(TAG, "Intent does not contain resultData");
stopSelf();
}
return START_STICKY;
}
private void startScreenRecording(Intent resultData) {
mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
mediaProjection = mediaProjectionManager.getMediaProjection(RESULT_OK, resultData);
if (mediaProjection == null) {
Log.e(TAG, "MediaProjection is null");
stopSelf();
return;
}
try {
setupMediaCodec();
createVirtualDisplay();
} catch (IOException e) {
Log.e(TAG, "Error setting up MediaCodec", e);
stopSelf();
}
}
private void setupMediaCodec() throws IOException {
mBufferInfo = new MediaCodec.BufferInfo();
mEncoder = MediaCodec.createEncoderByType(VIDEO_MIME_TYPE);
MediaFormat format = MediaFormat.createVideoFormat(VIDEO_MIME_TYPE, VIDEO_WIDTH, VIDEO_HEIGHT);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, VIDEO_BIT_RATE);
format.setInteger(MediaFormat.KEY_FRAME_RATE, VIDEO_FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, VIDEO_I_FRAME_INTERVAL);
mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mInputSurface = mEncoder.createInputSurface();
mEncoder.start();
}
private void createVirtualDisplay() {
mediaProjection.createVirtualDisplay(
"ScreenRecordingService",
VIDEO_WIDTH, VIDEO_HEIGHT, getResources().getDisplayMetrics().densityDpi,
android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mInputSurface, null, null);
scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleWithFixedDelay(this::encodeAndSendData, 0, 1, TimeUnit.SECONDS); // Adjusted to 1 second
}
private void encodeAndSendData() {
int outputBufferIndex = mEncoder.dequeueOutputBuffer(mBufferInfo, 10000); // Adjusted timeout to 10 seconds
while (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = mEncoder.getOutputBuffer(outputBufferIndex);
if (outputBuffer != null) {
byte[] outData = new byte[mBufferInfo.size];
outputBuffer.get(outData);
outputBuffer.clear();
if (isH264(outData) && isBound) {
//Log.d(TAG, "encodeAndSendData: WebSocketService " + outData.length + " bytes");
webSocketService.sendData(outData);
}
mEncoder.releaseOutputBuffer(outputBufferIndex, false);
}
outputBufferIndex = mEncoder.dequeueOutputBuffer(mBufferInfo, 10000);
}
}
private boolean isH264(byte[] data) {
if (data.length < 4) return false;
return (data[0] == 0 && data[1] == 0 && (data[2] == 1 || (data[2] == 0 && data[3] == 1)));
}
private void startForegroundService() {
// Create a notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Screen Recording")
.setContentText("Recording in progress...")
.setSmallIcon(R.mipmap.ic_launcher) // Use a proper icon for your notification
.setPriority(NotificationCompat.PRIORITY_LOW);
// Start the service in the foreground
startForeground(1, builder.build());
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Log.d(TAG, "Service destroyed");
if (mediaProjection != null) {
Log.d(TAG, "Stopping MediaProjection");
mediaProjection.stop();
}
if (mEncoder != null) {
Log.d(TAG, "Stopping MediaCodec");
mEncoder.stop();
mEncoder.release();
}
if (scheduler != null) {
Log.d(TAG, "Shutting down scheduler");
scheduler.shutdown();
}
if (isBound) {
unbindService(connection);
isBound = false;
}
super.onDestroy();
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
WebSocketService.LocalBinder binder = (WebSocketService.LocalBinder) service;
webSocketService = binder.getService();
isBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
}
};
}
Upvotes: 1
Views: 32