В моем приложении я хочу использовать функцию захвата видео с экрана устройства.
Я записываю записанные видеокоды в один сервис, но на некоторых устройствах после сохранения захваченного видео и его воспроизведения искажаем записанное видео.
Я вставляю сюда свои сервисные коды и знаю, что мои коды огромны.
Но на самом деле мне нужна помощь.
Воспроизведение сохраненного видео с эффектом изображения!
Нажмите, чтобысм. изображение
Мои сервисные коды:
public class RecorderService extends Service {
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
private int WIDTH, HEIGHT, FPS, DENSITY_DPI;
private int BITRATE;
private boolean isBound = false;
private String audioRecSource;
private String SAVEPATH, videoName;
private AudioManager mAudioManager;
private FloatingControlService floatingControlService;
static {
ORIENTATIONS.append(Surface.ROTATION_0, 0);
ORIENTATIONS.append(Surface.ROTATION_90, 90);
ORIENTATIONS.append(Surface.ROTATION_180, 180);
ORIENTATIONS.append(Surface.ROTATION_270, 270);
}
private int screenOrientation;
private String saveLocation;
private boolean isRecording;
private NotificationManager mNotificationManager;
Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message message) {
Toast.makeText(RecorderService.this, “Capture video“, Toast.LENGTH_SHORT).show();
}
};
private Intent data;
private int result;
private WindowManager window;
private MediaProjection mMediaProjection;
private VirtualDisplay mVirtualDisplay;
private MediaProjectionCallback mMediaProjectionCallback;
private MediaRecorder mMediaRecorder;
//Service connection to manage the connection state between this service and the bounded service
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//Get the service instance
FloatingControlService.ServiceBinder binder = (FloatingControlService.ServiceBinder) service;
floatingControlService = binder.getService();
isBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
floatingControlService = null;
isBound = false;
}
};
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
videoName = GoodPrefs.getInstance().getString(ConstKeys.TEST_NAME, "Testady");
//return super.onStartCommand(intent, flags, startId);
//Find the action to perform from intent
switch (intent.getAction()) {
case ConstKeys.SCREEN_RECORDING_START:
if (!isRecording) {
screenOrientation = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
data = intent.getParcelableExtra(ConstKeys.RECORDER_INTENT_DATA);
result = intent.getIntExtra(ConstKeys.RECORDER_INTENT_RESULT, Activity.RESULT_OK);
getValues();
startRecording();
openTestApp();
} else {
Toast.makeText(this, “Recording already used“, Toast.LENGTH_SHORT).show();
}
break;
case ConstKeys.SCREEN_RECORDING_PAUSE:
break;
case ConstKeys.SCREEN_RECORDING_RESUME:
break;
case ConstKeys.SCREEN_RECORDING_STOP:
stopRecording();
break;
}
return START_STICKY;
}
private void openTestApp() {
String runnableApp = GoodPrefs.getInstance().getString(ConstKeys.TEST_RUNNABLE, "");
String packageName = GoodPrefs.getInstance().getString(ConstKeys.TEST_PACKAGE_NAME, "");
if (!App.isEmptyString(runnableApp)) {
//Browser
if (runnableApp.equals(ConstKeys.BROWSER)) {
String siteUrl = GoodPrefs.getInstance().getString(ConstKeys.TEST_DIRECTION, "");
startActivity(App.openUrlWithBrowser(this, siteUrl, packageName));
} else {
startActivity(App.openAppWithPackage(this, packageName));
}
}
}
private void stopRecording() {
stopScreenSharing();
}
private void startRecording() {
//Initialize MediaRecorder class and initialize it with preferred configuration
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setOnErrorListener((mr, what, extra) -> {
Toast.makeText(this, "Field capture video", Toast.LENGTH_SHORT).show();
destroyMediaProjection();
});
initRecorder();
//Set Callback for MediaProjection
mMediaProjectionCallback = new MediaProjectionCallback();
MediaProjectionManager mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
//Initialize MediaProjection using data received from Intent
mMediaProjection = mProjectionManager.getMediaProjection(result, data);
mMediaProjection.registerCallback(mMediaProjectionCallback, null);
/* Create a new virtual display with the actual default display
* and pass it on to MediaRecorder to start recording */
mVirtualDisplay = createVirtualDisplay();
try {
mMediaRecorder.start();
//Floating button service
Intent floatingControlsIntent = new Intent(this, FloatingControlService.class);
startService(floatingControlsIntent);
bindService(floatingControlsIntent,
serviceConnection, BIND_AUTO_CREATE);
//Set the state of the recording
if (isBound)
floatingControlService.setRecordingState(ConstKeys.RecordingState.RECORDING);
isRecording = true;
} catch (IllegalStateException e) {
Toast.makeText(this, “Field capture video“, Toast.LENGTH_SHORT).show();
isRecording = false;
mMediaProjection.stop();
stopSelf();
}
}
//Virtual display created by mirroring the actual physical display
private VirtualDisplay createVirtualDisplay() {
return mMediaProjection.createVirtualDisplay("MainActivity",
WIDTH, HEIGHT, DENSITY_DPI,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mMediaRecorder.getSurface(), null /*Callbacks*/, null
/*Handler*/);
}
public int getBestSampleRate() {
AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
String sampleRateString = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
int samplingRate = (sampleRateString == null) ? 44100 : Integer.parseInt(sampleRateString);
return samplingRate;
}
private boolean getMediaCodecFor(String format) {
MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
MediaFormat mediaFormat = MediaFormat.createVideoFormat(
format,
WIDTH,
HEIGHT
);
String encoder = list.findEncoderForFormat(mediaFormat);
if (encoder == null) {
Log.d("Null Encoder: ", format);
return false;
}
Log.d("Encoder", encoder);
return !encoder.startsWith("OMX.google");
}
private int getBestVideoEncoder() {
int VideoCodec = MediaRecorder.VideoEncoder.DEFAULT;
if (getMediaCodecFor(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
VideoCodec = MediaRecorder.VideoEncoder.HEVC;
}
} else if (getMediaCodecFor(MediaFormat.MIMETYPE_VIDEO_AVC))
VideoCodec = MediaRecorder.VideoEncoder.H264;
return VideoCodec;
}
/* Initialize MediaRecorder with desired default values and values set by user. Everything is
* pretty much self explanatory */
private void initRecorder() {
boolean mustRecAudio = false;
try {
String audioBitRate = "128";
String audioSamplingRate = "68000";
String audioChannel = "1";
switch (audioRecSource) {
case "1":
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mustRecAudio = true;
break;
case "2":
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mMediaRecorder.setAudioEncodingBitRate(Integer.parseInt(audioBitRate));
mMediaRecorder.setAudioSamplingRate(Integer.parseInt(audioSamplingRate));
mMediaRecorder.setAudioChannels(Integer.parseInt(audioChannel));
mustRecAudio = true;
break;
case "3":
mAudioManager.setParameters("screenRecordAudioSource=8");
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.REMOTE_SUBMIX);
mMediaRecorder.setAudioEncodingBitRate(Integer.parseInt(audioBitRate));
mMediaRecorder.setAudioSamplingRate(Integer.parseInt(audioSamplingRate));
mMediaRecorder.setAudioChannels(Integer.parseInt(audioChannel));
mustRecAudio = true;
break;
}
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setOutputFile(SAVEPATH);
mMediaRecorder.setVideoSize(WIDTH, HEIGHT);
mMediaRecorder.setVideoEncoder(getBestVideoEncoder());
mMediaRecorder.setMaxFileSize(getFreeSpaceInBytes());
if (mustRecAudio)
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mMediaRecorder.setVideoEncodingBitRate(BITRATE);
mMediaRecorder.setVideoFrameRate(FPS);
mMediaRecorder.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
private long getFreeSpaceInBytes() {
StatFs FSStats = new StatFs(saveLocation);
long bytesAvailable = FSStats.getAvailableBytes();// * FSStats.getBlockCountLong();
return bytesAvailable;
}
//Start service as a foreground service. We dont want the service to be killed in case of low memory
private void startNotificationForeGround(Notification notification, int ID) {
startForeground(ID, notification);
}
//Update existing notification with its ID and new Notification data
private void updateNotification(Notification notification, int ID) {
getManager().notify(ID, notification);
}
private NotificationManager getManager() {
if (mNotificationManager == null) {
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
return mNotificationManager;
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
//Get user's choices for user choosable settings
public void getValues() {
String res = getResolution();
setWidthHeight(res);
FPS = 25;
BITRATE = 7130317;
audioRecSource = "1";
saveLocation = Environment.getExternalStorageDirectory() + File.separator + ConstKeys.APPDIR;
File saveDir = new File(saveLocation);
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) && !saveDir.isDirectory()) {
saveDir.mkdirs();
}
String saveFileName = getFileSaveName();
SAVEPATH = saveLocation + File.separator + saveFileName + ".mp4";
}
/* The PreferenceScreen save values as string and we save the user selected video resolution as
* WIDTH x HEIGHT. Lets split the string on 'x' and retrieve width and height */
private void setWidthHeight(String res) {
String[] widthHeight = res.split("x");
String orientationPrefs = "auto";
switch (orientationPrefs) {
case "auto":
if (screenOrientation == 0 || screenOrientation == 2) {
WIDTH = Integer.parseInt(widthHeight[0]);
HEIGHT = Integer.parseInt(widthHeight[1]);
} else {
HEIGHT = Integer.parseInt(widthHeight[0]);
WIDTH = Integer.parseInt(widthHeight[1]);
}
break;
case "portrait":
WIDTH = Integer.parseInt(widthHeight[0]);
HEIGHT = Integer.parseInt(widthHeight[1]);
break;
case "landscape":
HEIGHT = Integer.parseInt(widthHeight[0]);
WIDTH = Integer.parseInt(widthHeight[1]);
break;
}
}
//Get the device resolution in pixels
private String getResolution() {
DisplayMetrics metrics = new DisplayMetrics();
window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
window.getDefaultDisplay().getRealMetrics(metrics);
DENSITY_DPI = metrics.densityDpi;
int width = metrics.widthPixels;
width = 480;
float aspectRatio = getAspectRatio(metrics);
int height = calculateClosestHeight(width, aspectRatio);
//String res = width + "x" + (int) (width * getAspectRatio(metrics));
String res = width + "x" + height;
return res;
}
private int calculateClosestHeight(int width, float aspectRatio) {
int calculatedHeight = (int) (width * aspectRatio);
if (calculatedHeight / 16 != 0) {
int quotient = calculatedHeight / 16;
calculatedHeight = 16 * quotient;
}
return calculatedHeight;
}
private float getAspectRatio(DisplayMetrics metrics) {
float screen_width = metrics.widthPixels;
float screen_height = metrics.heightPixels;
float aspectRatio;
if (screen_width > screen_height) {
aspectRatio = screen_width / screen_height;
} else {
aspectRatio = screen_height / screen_width;
}
return aspectRatio;
}
//Return filename of the video to be saved formatted as chosen by the user
private String getFileSaveName() {
String filename = "yyyyMMdd_HHmmss";
//Required to handle preference change
filename = filename.replace("hh", "HH");
String prefix = videoName;
Date today = Calendar.getInstance().getTime();
SimpleDateFormat formatter = new SimpleDateFormat(filename);
return prefix + "_" + formatter.format(today);
}
/* Its weird that android does not index the files immediately once its created and that causes
* trouble for user in finding the video in gallery. Let's explicitly announce the file creation
* to android and index it */
private void indexFile() {
//Create a new ArrayList and add the newly created video file path to it
ArrayList<String> toBeScanned = new ArrayList<>();
toBeScanned.add(SAVEPATH);
String[] toBeScannedStr = new String[toBeScanned.size()];
toBeScannedStr = toBeScanned.toArray(toBeScannedStr);
//Request MediaScannerConnection to scan the new file and index it
MediaScannerConnection.scanFile(this, toBeScannedStr, null, (path, uri) -> {
//Show toast on main thread
Message message = mHandler.obtainMessage();
message.sendToTarget();
stopSelf();
});
}
//Stop and destroy all the objects used for screen recording
private void destroyMediaProjection() {
this.mAudioManager.setParameters("screenRecordAudioSource=0");
try {
mMediaRecorder.stop();
indexFile();
} catch (RuntimeException e) {
if (new File(SAVEPATH).delete())
Toast.makeText(this, “Failed to store video“, Toast.LENGTH_SHORT).show();
} finally {
mMediaRecorder.reset();
mVirtualDisplay.release();
mMediaRecorder.release();
if (mMediaProjection != null) {
mMediaProjection.unregisterCallback(mMediaProjectionCallback);
mMediaProjection.stop();
mMediaProjection = null;
}
stopSelf();
}
isRecording = false;
}
private void stopScreenSharing() {
if (mVirtualDisplay == null) {
return;
}
destroyMediaProjection();
}
private class MediaProjectionCallback extends MediaProjection.Callback {
@Override
public void onStop() {
stopScreenSharing();
}
}
}
Как я могу это исправить?