Take this block of example code:
// from window manager
val recordWidth = screenWidth
val recordHeight = screenHeight
val projection: MediaProjection = // retrieved from API
val mediaRecorder = MediaRecorder().apply {
setVideoSource(SURFACE)
setOutputFormat(MPEG_4)
setVideoFrameRate(frameRate) // e.g. 30
setVideoEncoder(H264)
setVideoSize(recordWidth, recordHeight)
setVideoEncodingBitRate(videoBitRate)
setOutputFile(outputFile)
prepare()
}
val virtualDisplay: VirtualDisplay = projection?.createVirtualDisplay(
"Example",
screenWidth,
screenHeight,
screenDensity,
VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mediaRecorder.surface,
null,
null
)
mediaRecorder.start()
This all works well, as long as screenWidth and screenHeight match that of the display.
If I change recordWidth and recordHeight (which are passed to the MediaRecorder with setVideoSize(Int, Int)), then all goes wrong. The recorded video tends to contain just the upper left section of the overall screen.
So my main questions:
Does something special have to be done to reduce resolution when
recording the screen? Even if I keep the aspect ratio with the
screen, it doesn't seem to work.
Some width/height values cause a
crash - is there an API to retrieve supported screen recording
sizes? I know Camera provides one, but this isn't really using the
Camera APIs.
I think you can refer this one
this.mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, resultData);
Log.d(TAG, "startRecording...");
this.mVideoBufferInfo = new MediaCodec.BufferInfo();
MediaFormat mediaFormat = MediaFormat.createVideoFormat(format, width, height);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, FPS);
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 0);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
Related
For some reason, I want video has fixed resolution in landscape whether i recorded in portrait mode or landscape mode. like this picture:
Picture
I try mediaRecorder.setVideoSize(640,480) to set fixed resolution for my videos. But the output video still in 480x640. output video
mediaRecorder.setOutputFormat(camcorderProfile.fileFormat);
if (enableAudio) {
mediaRecorder.setAudioEncoder(camcorderProfile.audioCodec);
mediaRecorder.setAudioEncodingBitRate(camcorderProfile.audioBitRate);
mediaRecorder.setAudioSamplingRate(camcorderProfile.audioSampleRate);
}
mediaRecorder.setVideoEncoder(camcorderProfile.videoCodec);
mediaRecorder.setVideoEncodingBitRate(camcorderProfile.videoBitRate);
mediaRecorder.setVideoFrameRate(camcorderProfile.videoFrameRate);
mediaRecorder.setVideoSize(
640, 480);
My expectation is when i record video in portrait mode the output video has fixed resolution.
Edit: I found a similar questions in here but still have expected answer. here
in Ios i use AVMutableVideoComposition to convert my video after recorded. Is there anyway to convert video like this in Android
let vComp = AVMutableVideoComposition()
vComp.renderSize = CGSize(width: CustomConsts.SAVE_WIDTH, height: CustomConsts.SAVE_HEIGHT)
vComp.frameDuration = CMTimeMake(value: 1, timescale: Int32(CustomConsts.SAVE_FRAME_RATE)) // fps
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: vTrack.timeRange.duration)
let transformer = AVMutableVideoCompositionLayerInstruction(assetTrack: vTrack)
var videoSize = vTrack.naturalSize
videoSize = CGSize(width: videoSize.height, height: videoSize.width)
let ratio = CGFloat(CustomConsts.SAVE_WIDTH) / videoSize.width
let resizedVideoSize = CGSize(width: videoSize.width * ratio, height: videoSize.height * ratio)
let t1 = CGAffineTransform(rotationAngle: 90 * CGFloat.pi / 180)
let t2 = CGAffineTransform(scaleX: ratio, y: ratio)
let t3 = CGAffineTransform(translationX: resizedVideoSize.width, y: -(resizedVideoSize.height - CGFloat(CustomConsts.SAVE_HEIGHT)) / 2)
transformer.setTransform(t1.concatenating(t2).concatenating(t3), at: CMTime.zero)
instruction.layerInstructions = [transformer]
vComp.instructions = [instruction]
I have been stuck in this issue for days.
I followed this Android's official camera-sample in Kotlin:
android's camera-sample
I raised an issue on github issue on 11 Feb 2020 but haven't received any feedback.
My problem is:
I used the sample as it is and only changed val cameraId = manager.cameraIdList[0] to val cameraId = manager.cameraIdList[1] for front camera.
NOTE: It does not happen in rear camera.
The front camera does not work and shows black bar on
devices tested:
Emulator: Pixel C API 29
Device: Galaxy Tab S2
Mode: Portrait
I wanted a full screen view, so when I don't set the aspect ratio of AutoTextureView in the commented line below, the video takes full screen but is now stretched.
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
//I only have portrait mode
} else {
//textureView.setAspectRatio(previewSize.height, previewSize.width)
}
Is there a way to set full screen mode without any stretching or in a correct aspect ratio?
I have been through following solutions in slack and none worked for me:
Camera 2 : Unable to record video in full screen?
Camera2 API Make Preview Fill Entire View
Android Camera2 API stretching the preview
After working for days. Camera2 full screen preview and image capture helped me solve the problem.
Setting onMeasure in AutoFitTextureView as:
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val width = View.MeasureSpec.getSize(widthMeasureSpec)
val height = View.MeasureSpec.getSize(heightMeasureSpec)
if (ratioWidth == 0 || ratioHeight == 0) {
setMeasuredDimension(width, height)
} else {
if (width > ((height * ratioWidth) / ratioHeight)) {
setMeasuredDimension(width, (width * ratioHeight) / ratioWidth)
} else {
setMeasuredDimension((height * ratioWidth) / ratioHeight, height)
}
}
}
Above code makes the screen full size but had problem of preview not
being at the centre
So I translated as follows in configureTransform(viewWidth: Int, viewHeight: Int)
// adjust the x and y to centre the preview
val screenWidth = resources.displayMetrics.widthPixels
val xShift = (viewWidth - screenWidth)/2
val screenHeight = resources.displayMetrics.heightPixels
val yShift = (viewHeight - screenHeight)/2
matrix.setTranslate(-xShift.toFloat(), -yShift.toFloat())
textureView.setTransform(matrix)
Following are the screenshots when using texture view in camera2 apis.In full screen the preview stretches,but it works when using lower resolution(second image).
How to use this preview in full screen without stretching it.
Below answer assumes you are in portrait mode only.
Your question is
How to use the preview in full-screen without stretching it
Let's break it down to 2 things:
You want the preview to fill the screen
The preview cannot be distorted
First you need to know that this is logically impossible without crop, if your device's viewport has a different aspect ratio with any available resolution the camera provides.
So I would assume you accept cropping the preview.
Step 1: Get a list of available resolutions
StreamConfigurationMap map = mCameraCharacteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
throw new IllegalStateException("Failed to get configuration map: " + mCameraId);
}
Size[] sizes = map.getOutputSizes(SurfaceTexture.class);
Now you get a list of available resolutions (Sizes) of your device's camera.
Step 2: Find the best aspect ratio
The idea is to loop the sizes and see which one best fits. You probably need to write your own implementation of "best fits".
I am not going to provide any code here since what I have is quite different from your use case. But ideally, it should be something like this:
Size findBestSize (Size[] sizes) {
//Logic goes here
}
Step 3: Tell the Camera API that you want to use this size
//...
textureView.setBufferSize(bestSize.getWidth(), bestSize.getHeight());
Surface surface = textureView.getSurface();
try {
mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
mSessionCallback, null);
} catch (final Exception e) {
//...
}
Step 4: Make your preview extends beyond your viewport
This is then nothing related to the Camera2 API. We "crop" the preview by letting the SurfaceView / TextureView extends beyond device's viewport.
First place your SurfaceView or TextureView in a RelativeLayout.
Use the below to extend it beyond the screen, after you get the aspect ratio from step 2.
Note that in this case you probably need to know this aspect ratio before you even start the camera.
//Suppose this value is obtained from Step 2.
//I simply test here by hardcoding a 3:4 aspect ratio, where my phone has a thinner aspect ratio.
float cameraAspectRatio = (float) 0.75;
//Preparation
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight = metrics.heightPixels;
int finalWidth = screenWidth;
int finalHeight = screenHeight;
int widthDifference = 0;
int heightDifference = 0;
float screenAspectRatio = (float) screenWidth / screenHeight;
//Determines whether we crop width or crop height
if (screenAspectRatio > cameraAspectRatio) { //Keep width crop height
finalHeight = (int) (screenWidth / cameraAspectRatio);
heightDifference = finalHeight - screenHeight;
} else { //Keep height crop width
finalWidth = (int) (screenHeight * cameraAspectRatio);
widthDifference = finalWidth - screenWidth;
}
//Apply the result to the Preview
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) cameraView.getLayoutParams();
lp.width = finalWidth;
lp.height = finalHeight;
//Below 2 lines are to center the preview, since cropping default occurs at the right and bottom
lp.leftMargin = - (widthDifference / 2);
lp.topMargin = - (heightDifference / 2);
cameraView.setLayoutParams(lp);
If you don't care about the result of Step 2, you can actually ignore Step 1 to Step 3 and simply use a library out there, as long as you can configure its aspect ratio. (It looks like this one is the best, but I haven't tried yet)
I have tested using my forked library. Without modifying any code of my library, I managed to make the preview fullscreen just by using Step 4:
Before using Step 4:
After using Step 4:
And the preview just after taking a photo will not distort as well, because the preview is also extending beyond your screen.
But the output image will include area that you cannot see in the preview, which makes perfect sense.
The code of Step 1 to Step 3 are generally referenced from Google's CameraView.
That's a common problem on some devices. I've noticed it mostly on samsung. You may use a trick with setting transformation on your TextureView to make it centerCrop like ImageView behaviour
I also faced similar situation, but this one line solved my problem
view_finder.preferredImplementationMode = PreviewView.ImplementationMode.TEXTURE_VIEW
in your xml:
<androidx.camera.view.PreviewView
android:id="#+id/view_finder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
For camera implementation using cameraX you can refer
https://github.com/android/camera-samples/tree/master/CameraXBasic
I figured out what was your poroblem. You were probably trying something like this:
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int j) {
cam.startPreview(surfaceTexture, i, j);
cam.takePicture();
}
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) { }
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { return false; }
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { }
});
I am staring to develop an app which monitors the camera preview, and does some image processing on it and displays int on a canvas. Just as a diagnostic I have the following code:
camera = Camera.open();
ImageFormat imf = new ImageFormat();
Camera.Parameters param = camera.getParameters();
param.setPreviewSize(128, 128);
preview_format = param.getPreviewFormat();
Camera.Size sz = param.getPreviewSize();
myimage = new int[sz.width*sz.height];
At run time it reports that preview_format is 17 which I understand is "NV21".
Later I have:
camera.setPreviewCallback(new PreviewCallback()
{
public void onPreviewFrame(byte[] _data, Camera _camera)
{
YUV_NV21_TO_RGB(myimage , _data, 128, 128) ;
}
});
The function YUV_NV21_TO_RGB was taken from here.
Meanwhile in another thread I have:
canvas.drawBitmap(
myimage, // the int array
0, // where to start in the array
128, // the stride ???
200, // x coord of where to display
200, // y coord of where to display
128, // wid
128, // ht
false, // alpha used?
null); // the paint used
The resulting image can be seen amongst other diagnostics in the square below. The stripes change as I move the phone around and appear to in some way correspond to what the camera is pointing at, but clearly it has been mangled. I tried using an alternative function found here, and another from wikipedia, but with seemingly identical results. Any ideas?
EDIT: One thought I had was that perhaps NV21 may not completely specify the format - maybe its a class of formats, where you need to go on and specify the bits per pixel or similar.
EDIT: An extra clue - if I cover the camera completely, the square goes entirely pure green.
Your preview size is not 128 by 128 because you fail to set it. You set it on the Camera.Parameters instance but you don't apply it to the camera.
You need to add the following line:
camera.setParameters(param);
And it's probably safe to get the parameters directly from the Camera instance:
preview_format = camera.getPreviewFormat();
Camera.Size sz = camera.getPreviewSize();
I searched for past two days and i was not successful yet .
I my case , i want to check the camera pixel resolution/Megapixels . If the camera's Mp is more than 4 then i need to re-size and upload .
Here is my code :
//to check the resolution
Camera mcamera ;
mcamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
Camera.Parameters param = mcamera.getParameters();
Camera.Size size = param.getPictureSize();
cam_height = size.height ;
cam_width = size.width ;
mcamera.release();
// my functionality
BitmapFactory.Options resample = new BitmapFactory.Options();
if(cam_height > pict_height || cam_width > pict_width )
resample.inSampleSize = 2; // whatever number seems appropriate 2 means 1/2 of the original
else
resample.inSampleSize = 1;
capturedimg = BitmapFactory.decodeFile(fileUri.getPath() , resample);
resized_uri = bitmaptouri(capturedimg);
but this returns only the picture resolution which is the same as the Screen resolution of the mobile but i want the Mobile camera's resolution .
Any related answers are welcomed , Thanks in advance .
How about getSupportedPictureSizes()?
First find height and width like below:
android.hardware.Camera.Parameters parameters = camera.getParameters();
android.hardware.Camera.Size size = parameters.getPictureSize();
int height = size.height;
int width = size.width;
then get mega pixel using below equation:
int mg = height * width / 1024000;
where mg is your mega pixels.
First check the supported picture sizes available for the Camera using Camera.Parameters. There is a function called getSupportedPictureSizes() in Camera Parameters.
For e.g:
List<Camera.Size> mList = mParams.getSupportedPictureSizes();
Camera.Size mSize = mList.get(mList.size() - 1);
From the mList you get all the supported Picture Sizes. The final one in the list will be the largest possible resolution.
Try the code from here. It returns resolution in mp for back camera. You should use getSupportedPictureSize instead of getPictureSize
https://stackoverflow.com/a/27000029/1554031