Android and SurfaceView. Wrong size - android

I have some problems with SurfaceView resizing in Android 2.3. versions. I am working with video player I need to resize my SurfaceView when video frame size doesn't match surface size. I have Fragment with SurfaceView inside it. Fragment doesnt match full screen, just some part of it. For Android 4 versions everything is OK, but in Android 2.3. after resizing my SurfaceView is bigger than its FrameLayout parent. I have no ideas why this is happening.
Here is interface which allowas video size init
/**
* This method is called by native vout to request a surface resize when frame size doesn't match surface size.
* #param width Frame width
* #param height Frame height
* #param visible_width Visible frame width
* #param visible_height Visible frame height
* #param sar_num Surface aspect ratio numerator
* #param sar_den Surface aspect ratio denominator
*/
void setSurfaceSize(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den);
//This method is an implementation of Interface
#Override
public void setSurfaceSize(int width, int height, int visible_width, int visible_height, int sar_num, int sar_den) {
if (width * height == 0)
return;
// store video size
mVideoHeight = height;
mVideoWidth = width;
mVideoVisibleHeight = visible_height;
mVideoVisibleWidth = visible_width;
mSarNum = sar_num;
mSarDen = sar_den;
Message msg = mHandler.obtainMessage(SURFACE_SIZE);
mHandler.sendMessage(msg);
}
And the second - change my SurfaceView size.
private void changeSurfaceSize() {
int sw;
int sh;
// get screen size
if (mPresentation == null) {
Log.d("MyLog", "Presentation is null");
sw = ((Activity)mainActivity).getWindow().getDecorView().getWidth();
sh = ((Activity)mainActivity).getWindow().getDecorView().getHeight();
} else {
Log.d("MyLog", "Presentation is not null");
sw = mPresentation.getWindow().getDecorView().getWidth();
sh = mPresentation.getWindow().getDecorView().getHeight();
}
double dw = sw, dh = sh;
boolean isPortrait;
if (mPresentation == null) {
// getWindow().getDecorView() doesn't always take orientation into account, we have to correct the values
isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
} else {
isPortrait = false;
}
if (sw > sh && isPortrait || sw < sh && !isPortrait) {
dw = sh;
dh = sw;
}
// sanity check
if (dw * dh == 0 || mVideoWidth * mVideoHeight == 0) {
Log.e(TAG, "Invalid surface size");
return;
}
// compute the aspect ratio
double ar, vw;
double density = (double)mSarNum / (double)mSarDen;
if (density == 1.0) {
/* No indication about the density, assuming 1:1 */
vw = mVideoVisibleWidth;
ar = (double)mVideoVisibleWidth / (double)mVideoVisibleHeight;
} else {
/* Use the specified aspect ratio */
vw = mVideoVisibleWidth * density;
ar = vw / mVideoVisibleHeight;
}
// compute the display aspect ratio
double dar = dw / dh;
switch (mCurrentSize) {
case SURFACE_BEST_FIT:
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_FIT_HORIZONTAL:
dh = dw / ar;
break;
case SURFACE_FIT_VERTICAL:
dw = dh * ar;
break;
case SURFACE_FILL:
break;
case SURFACE_16_9:
ar = 16.0 / 9.0;
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_4_3:
ar = 4.0 / 3.0;
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_ORIGINAL:
dh = mVideoVisibleHeight;
dw = vw;
break;
}
SurfaceView surface;
SurfaceView subtitlesSurface;
SurfaceHolder surfaceHolder;
SurfaceHolder subtitlesSurfaceHolder;
FrameLayout surfaceFrame;
if (mPresentation == null) {
surface = mSurface;
subtitlesSurface = mSubtitlesSurface;
surfaceHolder = mSurfaceHolder;
subtitlesSurfaceHolder = mSubtitlesSurfaceHolder;
surfaceFrame = mSurfaceFrame;
} else {
surface = mPresentation.mSurface;
subtitlesSurface = mPresentation.mSubtitlesSurface;
surfaceHolder = mPresentation.mSurfaceHolder;
subtitlesSurfaceHolder = mPresentation.mSubtitlesSurfaceHolder;
surfaceFrame = mPresentation.mSurfaceFrame;
}
// force surface buffer size
surfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);
subtitlesSurfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);
// set display size
LayoutParams lp = surface.getLayoutParams();
lp.width = (int) Math.ceil(dw * mVideoWidth / mVideoVisibleWidth);
lp.height = (int) Math.ceil(dh * mVideoHeight / mVideoVisibleHeight);
surface.setLayoutParams(lp);
subtitlesSurface.setLayoutParams(lp);
// set frame size (crop if necessary)
lp = surfaceFrame.getLayoutParams();
lp.width = (int) Math.floor(dw);
lp.height = (int) Math.floor(dh);
surfaceFrame.setLayoutParams(lp);
surface.invalidate();
subtitlesSurface.invalidate();
}
Does anybody have any ideas, why could it happens so? I will be very glad for any help!!! Thank you!

Related

Camera preview appears stretched

Im trying to use camera 2 with and AutoFitTextureView from the google sample.
Im setting it up like this
private void setUpCameraOutputs() {
CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
try {
if (manager != null) {
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
// We don't use a front facing camera in this sample.
//noinspection ConstantConditions
if (characteristics.get(LENS_FACING) == LENS_FACING_FRONT) continue;
StreamConfigurationMap map = characteristics.get(SCALER_STREAM_CONFIGURATION_MAP);
if(map != null) {
// For still image captures, we use the largest available size.
List<Size> outputSizes = Arrays.asList(map.getOutputSizes(sImageFormat));
Size largest = Collections.max(outputSizes, new CompareSizesByArea());
mImageReader = ImageReader.newInstance(640, 480, sImageFormat, 8);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
// Danger, W.R.! Attempting to use too large a preview size could exceed the camera
// bus' bandwidth limitation, resulting in gorgeous previews but the storage of
// garbage capture data.
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), largest.getWidth(), largest.getHeight(), largest);
//mPreviewSize = new Size(largest.getWidth(), largest.getHeight());
setAspectRatio2(mPreviewSize);
Logging.e(TAG, "WIDTH: " + mPreviewSize.getWidth() + " HEIGHT: " + mPreviewSize.getHeight());
mCameraId = cameraId;
}
//return;
}
}else{
Logging.e(TAG,"No Manager");
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void setAspectRatio(){
int orientation = mContext.getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
mTextureView.setAspectRatio(mPreviewSize.getWidth(),mPreviewSize.getHeight());
} else {
mTextureView.setAspectRatio(mPreviewSize.getHeight(),mPreviewSize.getWidth());
}
}
private void setAspectRatio2(Size largest){
float cameraAspectRatio = (float) largest.getHeight() / largest.getWidth();
//Preparation
DisplayMetrics metrics = new DisplayMetrics();
WindowManager wm = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE));
wm.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
LayoutParams lp = (FrameLayout.LayoutParams) mTextureView.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);
mTextureView.setLayoutParams(lp);
}
private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
// Collect the supported resolutions that are at least as big as the preview Surface
List<Size> bigEnough = new ArrayList<>();
int w = aspectRatio.getWidth();
int h = aspectRatio.getHeight();
double ratio = (double) h / w;
for (Size option : choices) {
double optionRatio = (double) option.getHeight() / option.getWidth();
if (ratio == optionRatio) {
bigEnough.add(option);
}
}
// Pick the smallest of those, assuming we found any
if (bigEnough.size() > 0) {
return Collections.min(bigEnough, new CompareSizesByArea());
} else {
Logging.e(TAG, "Couldn't find any suitable preview size");
return choices[1];
}
}
I've tried to set the aspect ratio with 2 different methods, but none of them seem to do what i need.
The preview takes all screen as i want, but camera preview always appear streeched, things look thiner.
I've tried a few sugestions from stackoverflow but still haven't got the perfect result.
There are a lot of libraries which ease the use of CameraView.
You can have a look at this one or this one for example.

How do you Fullscreen RTSP Stream in Android LibVLC?

I'm using mrmaffen's VLC-ANDROID-SDK to develop an RTSP streaming app.
https://github.com/mrmaffen/vlc-android-sdk
I've had a lot of success getting it working and running quite well, but the problem I'm having that I can't seem to shake is getting it to display the video feed in fullscreen on the SurfaceView, or even just in the center of the SurfaceView.
This is what I get:
http://s1378.photobucket.com/user/Jo_Han_Solo/media/Screenshot_20171214-125504_zps437k1kw2.png.html?filters[user]=146993343&filters[recent]=1&sort=1&o=1
The black window is the total size of the screen, I want that video to fill the screen and hopefully always fill from center, but I can't figure out how to do it.
Anyone have any experience with anything like this and knows how to fix it?
I kind of solved the problem but in a bit of a dodgy way, it's far from complete but considering the lack of knowledge and information on the topic I thought this might help someone for the time being.
Find the size of your screen.
Set up your final IVLCOut to incorporate the screen size.
Adjust setScale to "fullscreen" the video stream.
To explain each task:
Setup your globals:
public class SingleStreamView extends AppCompatActivity implements
IVLCVout.Callback {
public int mHeight;
public int mWidth;
Secondly, in the onCreate task find your screen sizes of your device:
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
mHeight = displayMetrics.heightPixels;
mWidth = displayMetrics.widthPixels;
2.
Then go down to your "CreatePlayer" event and where you set up your video output:
// Set up video output
final IVLCVout vout = mMediaPlayer.getVLCVout();
vout.setVideoView(mSurface);
vout.setWindowSize(mWidth,mHeight);
vout.addCallback(this);
vout.attachViews();
The winning line that made it center in my surface was the "vout.setWindowSize(mWidth,mHeight);"
Then I simply used the setscale option to "fullscreen" the video. That said, it's a bit of a hack way of doing it, and I would like to try and figure out a way to grab the codec information so to dynamically set the scale of the video and that way automatically fullscreen every size video stream to any size screen but for now this will work for known video stream resolutions, it will automatically adjust to the screen size of your phone.
Either way I found that with a Samsung Galaxy s8, a good scaling factor for a 640x480p RTSP stream was 1.8. Coded like so:
Media m = new Media(libvlc, Uri.parse(RTSP_ADDRESS));
m.setHWDecoderEnabled(true,false);
m.addOption(":network-caching=100");
m.addOption(":clock-jitter=0");
m.addOption(":clock-synchro=0");
m.addOption(":fullscreen");
mMediaPlayer.setMedia(m);
mMediaPlayer.setAspectRatio("16:9");
mMediaPlayer.setScale(1.8f);
mMediaPlayer.play();
Where you got "mMediaPlayer.setScale(1.8f);"
Hope this helps someone!
your solution seems interesting, however I'm facing the same issues, which I can't seem to solve (yet) with your approach.
Screenshots of what I got sofar can be seen at:
https://photos.app.goo.gl/9nKo22Mkc2SZq4SK9
I also want to (vertically) center an rtsp-video-stream in either landscape/portrait mode on a Samsung-XCover4 (with 720x1280 pixels) and on a device with minimum resolution of 320x480. The minimum Android SDK-version I would love to have it running is API-22 (Android 5.1.1).
The libvlc code for which I got the (embedded)VLC-player working, is based on 'de.mrmaffen:libvlc-android:2.1.12#aar'.
Given the above 'requirements', you can see the following behavior in the screenshots. The first two screenshots are on a Samsung-XCover4 (720x1280) where you can see that device-orientation=landscape clips the video and doesn't scale it, whereas the 3rd and 4th screenshot show that the same video-stream doesn't follow the SURFACE_BEST_FIT method (see code below for an explanation) on a device with small-resolution.
I would love to see an updateVideoSurfaces to handle the change in device-orientation or at least to show the entire video on startup.
The layout for my VLC-video-player (part of a vertical LinearLayout) is as follows:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.3"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:orientation="vertical">
<FrameLayout
android:id="#+id/video_surface_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:foregroundGravity="clip_horizontal|clip_vertical"
tools:ignore="true">
<ViewStub
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="#layout/surface_view"
android:id="#+id/surface_stub" />
<ViewStub
android:layout_width="1dp"
android:layout_height="1dp"
android:layout="#layout/surface_view"
android:id="#+id/subtitles_surface_stub" />
<ViewStub
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="#layout/texture_view"
android:id="#+id/texture_stub" />
</FrameLayout>
</LinearLayout>
The example code I got from de.mrmaffen uses an updateVideoSurfaces (see below java-code) which uses a number of SURFACE_XX method which to me seem to cover all scenarios with different device-orientations and resolution.
For some reason I can't figure out why this doesn't work and I suspect that the layout I'm using for the player (the FrameLayout/ViewStub's) may cause the issues.
I was wondering if you can shed some light on directions in order to make sure that the video stream will auto-scale/center on any device orientation/resolution.
The player-code I'm using is as follows:
package com.testing.vlc2player;
import ...
public class VLC2PlayerActivity extends AppCompatActivity implements IVLCVout.OnNewVideoLayoutListener,
IVLCVout.Callback {
private static final Logger log = LoggerFactory.getLogger(VLC2PlayerActivity.class);
private static final boolean USE_SURFACE_VIEW = true;
private static final boolean ENABLE_SUBTITLES = false;
private static final int SURFACE_BEST_FIT = 0;
private static final int SURFACE_FIT_SCREEN = 1;
private static final int SURFACE_FILL = 2;
private static final int SURFACE_16_9 = 3;
private static final int SURFACE_4_3 = 4;
private static final int SURFACE_ORIGINAL = 5;
private static final int CURRENT_SIZE = SURFACE_BEST_FIT;
private FrameLayout mVideoSurfaceFrame = null;
private SurfaceView mVideoSurface = null;
private SurfaceView mSubtitlesSurface = null;
private TextureView mVideoTexture = null;
private View mVideoView = null;
private final Handler mHandler = new Handler();
private View.OnLayoutChangeListener mOnLayoutChangeListener = null;
private LibVLC mLibVLC = null;
private MediaPlayer mMediaPlayer = null;
private int mVideoHeight = 0;
private int mVideoWidth = 0;
private int mVideoVisibleHeight = 0;
private int mVideoVisibleWidth = 0;
private int mVideoSarNum = 0;
private int mVideoSarDen = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_player);
setupVLCLayout();
}
private void setupVLCLayout() {
log.debug("...");
final ArrayList<String> args = new ArrayList<>();
args.add("-vvv");
mLibVLC = new LibVLC(this, args);
mMediaPlayer = new MediaPlayer(mLibVLC);
mVideoSurfaceFrame = findViewById(R.id.video_surface_frame);
if (USE_SURFACE_VIEW) {
ViewStub stub = findViewById(R.id.surface_stub);
mVideoSurface = (SurfaceView) stub.inflate();
if (ENABLE_SUBTITLES) {
stub = findViewById(R.id.subtitles_surface_stub);
mSubtitlesSurface = (SurfaceView) stub.inflate();
mSubtitlesSurface.setZOrderMediaOverlay(true);
mSubtitlesSurface.getHolder().setFormat(PixelFormat.TRANSLUCENT);
}
mVideoView = mVideoSurface;
} else {
ViewStub stub = findViewById(R.id.texture_stub);
mVideoTexture = (TextureView) stub.inflate();
mVideoView = mVideoTexture;
}
}
#Override
protected void onDestroy() {
super.onDestroy();
mMediaPlayer.release();
mLibVLC.release();
}
#Override
protected void onStart() {
super.onStart();
final IVLCVout vlcVout = mMediaPlayer.getVLCVout();
if (mVideoSurface != null) {
vlcVout.setVideoView(mVideoSurface);
if (mSubtitlesSurface != null) {
vlcVout.setSubtitlesView(mSubtitlesSurface);
}
} else {
vlcVout.setVideoView(mVideoTexture);
}
vlcVout.attachViews(this);
String url = getString(R.string.videoURL);
Uri uri = Uri.parse(url);
final Media media = new Media(mLibVLC, uri);
mMediaPlayer.setMedia(media);
media.release();
mMediaPlayer.play();
if (mOnLayoutChangeListener == null) {
mOnLayoutChangeListener = new View.OnLayoutChangeListener() {
private final Runnable mRunnable = new Runnable() {
#Override
public void run() {
updateVideoSurfaces();
}
};
#Override
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
mHandler.removeCallbacks(mRunnable);
mHandler.post(mRunnable);
}
}
};
}
mVideoSurfaceFrame.addOnLayoutChangeListener(mOnLayoutChangeListener);
}
#Override
protected void onStop() {
super.onStop();
if (mOnLayoutChangeListener != null) {
mVideoSurfaceFrame.removeOnLayoutChangeListener(mOnLayoutChangeListener);
mOnLayoutChangeListener = null;
}
mMediaPlayer.stop();
mMediaPlayer.getVLCVout().detachViews();
}
private void changeMediaPlayerLayout(int displayW, int displayH) {
log.debug("displayW={}, displayH={}", displayW, displayH);
/* Change the video placement using the MediaPlayer API */
int dispWd = displayW;
int dispHt = displayH;
dispWd = mVideoSurface.getWidth(); //Note: we do NOT want to use the entire display!
dispHt = mVideoSurface.getHeight();
switch (CURRENT_SIZE) {
case SURFACE_BEST_FIT:
mMediaPlayer.setAspectRatio(null);
mMediaPlayer.setScale(0);
break;
case SURFACE_FIT_SCREEN:
case SURFACE_FILL: {
Media.VideoTrack vtrack = mMediaPlayer.getCurrentVideoTrack();
if (vtrack == null) {
return;
}
final boolean videoSwapped = vtrack.orientation == Media.VideoTrack.Orientation.LeftBottom
|| vtrack.orientation == Media.VideoTrack.Orientation.RightTop;
if (CURRENT_SIZE == SURFACE_FIT_SCREEN) {
int videoW = vtrack.width;
int videoH = vtrack.height;
if (videoSwapped) {
int swap = videoW;
videoW = videoH;
videoH = swap;
}
if (vtrack.sarNum != vtrack.sarDen) {
videoW = videoW * vtrack.sarNum / vtrack.sarDen;
}
float ar = videoW / (float) videoH;
float dar = dispWd / (float) dispHt;
//noinspection unused
float scale;
if (dar >= ar) {
scale = dispWd / (float) videoW; /* horizontal */
} else {
scale = dispHt / (float) videoH; /* vertical */
}
log.debug("scale={}", scale);
mMediaPlayer.setScale(scale);
mMediaPlayer.setAspectRatio(null);
} else {
mMediaPlayer.setScale(0);
mMediaPlayer.setAspectRatio(!videoSwapped ? ""+dispWd+":"+dispHt
: ""+dispHt+":"+dispWd);
}
break;
}
case SURFACE_16_9:
mMediaPlayer.setAspectRatio("16:9");
mMediaPlayer.setScale(0);
break;
case SURFACE_4_3:
mMediaPlayer.setAspectRatio("4:3");
mMediaPlayer.setScale(0);
break;
case SURFACE_ORIGINAL:
mMediaPlayer.setAspectRatio(null);
mMediaPlayer.setScale(1);
break;
}
}
private void updateVideoSurfaces() {
log.debug("...");
int sw = getWindow().getDecorView().getWidth();
int sh = getWindow().getDecorView().getHeight();
// sanity check
if (sw * sh == 0) {
log.error("Invalid surface size");
return;
}
mMediaPlayer.getVLCVout().setWindowSize(sw, sh);
ViewGroup.LayoutParams lp = mVideoView.getLayoutParams();
if (mVideoWidth * mVideoHeight == 0) {
/* Case of OpenGL vouts: handles the placement of the video using MediaPlayer API */
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
mVideoView.setLayoutParams(lp);
lp = mVideoSurfaceFrame.getLayoutParams();
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
mVideoSurfaceFrame.setLayoutParams(lp);
changeMediaPlayerLayout(sw, sh);
return;
}
if (lp.width == lp.height && lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
/* We handle the placement of the video using Android View LayoutParams */
mMediaPlayer.setAspectRatio(null);
mMediaPlayer.setScale(0);
}
double dw = sw, dh = sh;
final boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
if (sw > sh && isPortrait || sw < sh && !isPortrait) {
dw = sh;
dh = sw;
}
// compute the aspect ratio
double ar, vw;
if (mVideoSarDen == mVideoSarNum) {
/* No indication about the density, assuming 1:1 */
vw = mVideoVisibleWidth;
ar = (double)mVideoVisibleWidth / (double)mVideoVisibleHeight;
} else {
/* Use the specified aspect ratio */
vw = mVideoVisibleWidth * (double)mVideoSarNum / mVideoSarDen;
ar = vw / mVideoVisibleHeight;
}
// compute the display aspect ratio
double dar = dw / dh;
switch (CURRENT_SIZE) {
case SURFACE_BEST_FIT:
if (dar < ar) {
dh = dw / ar;
} else {
dw = dh * ar;
}
break;
case SURFACE_FIT_SCREEN:
if (dar >= ar) {
dh = dw / ar; /* horizontal */
} else {
dw = dh * ar; /* vertical */
}
break;
case SURFACE_FILL:
break;
case SURFACE_16_9:
ar = 16.0 / 9.0;
if (dar < ar) {
dh = dw / ar;
} else {
dw = dh * ar;
}
break;
case SURFACE_4_3:
ar = 4.0 / 3.0;
if (dar < ar) {
dh = dw / ar;
} else {
dw = dh * ar;
}
break;
case SURFACE_ORIGINAL:
dh = mVideoVisibleHeight;
dw = vw;
break;
}
// set display size
lp.width = (int) Math.ceil(dw * mVideoWidth / mVideoVisibleWidth);
lp.height = (int) Math.ceil(dh * mVideoHeight / mVideoVisibleHeight);
mVideoView.setLayoutParams(lp);
if (mSubtitlesSurface != null) {
mSubtitlesSurface.setLayoutParams(lp);
}
// set frame size (crop if necessary)
lp = mVideoSurfaceFrame.getLayoutParams();
lp.width = (int) Math.floor(dw);
lp.height = (int) Math.floor(dh);
mVideoSurfaceFrame.setLayoutParams(lp);
mVideoView.invalidate();
if (mSubtitlesSurface != null) {
mSubtitlesSurface.invalidate();
}
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
#Override
public void onNewVideoLayout(IVLCVout vlcVout, int width, int height,
int visibleWidth, int visibleHeight,
int sarNum, int sarDen) {
log.debug("...");
mVideoWidth = width;
mVideoHeight = height;
mVideoVisibleWidth = visibleWidth;
mVideoVisibleHeight = visibleHeight;
mVideoSarNum = sarNum;
mVideoSarDen = sarDen;
updateVideoSurfaces();
}
#Override
public void onSurfacesCreated(IVLCVout vlcVout) {
log.debug("vlcVout={}", vlcVout);
}
/**
* This callback is called when surfaces are destroyed.
*/
public void onSurfacesDestroyed(IVLCVout vlcVout) {
log.debug("vlcVout={}", vlcVout);
}
public void onStopClientMonitoring(View view) {
// log.info("UI -> Stop monitoring clientId= ...");
// onBackPressed();
String androidSDKRelease = Build.VERSION.RELEASE;
int androidSDKInt = Build.VERSION.SDK_INT;
String androidInfo = String.format(Locale.getDefault(), "Android %s (Version %d)", androidSDKRelease, androidSDKInt);
String appVersionName = BuildConfig.VERSION_NAME;
String appName = getString(R.string.app_name);
String appInfoTitle = String.format(getString(R.string.app_info_title), appName);
String infoMsg = String.format(getString(R.string.app_info_message), appVersionName, androidInfo);
new AlertDialog.Builder(this).setTitle(appInfoTitle)
.setMessage(infoMsg)
.setPositiveButton(getString(R.string.button_ok), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// Dismiss dialog
dialog.dismiss();
}
})
.create()
.show();
}
}

LibVLC for Android - Stretch Live Stream RTSP to desired aspect ratio on SurfaceView

Okay so I'm using LibVLC for Android (with Android Studio) to receive the live RTSP stream of an IP Camera since VideoView doesn't quite support LIVE streams. I'm using a sample code from the VideoLAN people which can be found here:
https://code.videolan.org/videolan/libvlc-android-samples
And I've done a lot of investigation on the code to achieve 19:6 aspect ratio out of the 4:3 that my camera outputs. The reason why I'm trying to break the aspect ratio is because this IP Camera records 1280x720 pixels but outputs 640x480 through it's second stream. The problem is that the width isn't cropped, but stretched from the sides, so it looks kinda compressed tight.
I've tried setting the 4 alignParent options to true on the SurfaceView, but no results. Also tried to multiply some of the width variables I found there in the JavaActivity class code by 1.33333 which should theorically stretch the width, but nothing happened at all, not even an error or an exception. I also tried making a new class extending SurfaceView and tweaking with the onMeasure method, but no dice. This is the JavaActivity code as is from the example, (of course I've adapted mine to work with my project but with minor changes)
package org.videolan.javasample;
import android.annotation.TargetApi;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.FrameLayout;
import org.videolan.libvlc.IVLCVout;
import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.Media;
import org.videolan.libvlc.MediaPlayer;
import java.util.ArrayList;
public class JavaActivity extends AppCompatActivity implements IVLCVout.OnNewVideoLayoutListener {
private static final boolean USE_SURFACE_VIEW = true;
private static final boolean ENABLE_SUBTITLES = true;
private static final String TAG = "JavaActivity";
private static final String SAMPLE_URL = "http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_640x360.m4v";
// Not the actual RTSP Live Stream link but you know...
private static final int SURFACE_BEST_FIT = 0;
private static final int SURFACE_FIT_SCREEN = 1;
private static final int SURFACE_FILL = 2;
private static final int SURFACE_16_9 = 3;
private static final int SURFACE_4_3 = 4;
private static final int SURFACE_ORIGINAL = 5;
private static int CURRENT_SIZE = SURFACE_BEST_FIT;
private FrameLayout mVideoSurfaceFrame = null;
private SurfaceView mVideoSurface = null;
private SurfaceView mSubtitlesSurface = null;
private TextureView mVideoTexture = null;
private View mVideoView = null;
private final Handler mHandler = new Handler();
private View.OnLayoutChangeListener mOnLayoutChangeListener = null;
private LibVLC mLibVLC = null;
private MediaPlayer mMediaPlayer = null;
private int mVideoHeight = 0;
private int mVideoWidth = 0;
private int mVideoVisibleHeight = 0;
private int mVideoVisibleWidth = 0;
private int mVideoSarNum = 0;
private int mVideoSarDen = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ArrayList<String> args = new ArrayList<>();
args.add("-vvv");
mLibVLC = new LibVLC(this, args);
mMediaPlayer = new MediaPlayer(mLibVLC);
mVideoSurfaceFrame = (FrameLayout) findViewById(R.id.video_surface_frame);
if (USE_SURFACE_VIEW) {
ViewStub stub = (ViewStub) findViewById(R.id.surface_stub);
mVideoSurface = (SurfaceView) stub.inflate();
if (ENABLE_SUBTITLES) {
stub = (ViewStub) findViewById(R.id.subtitles_surface_stub);
mSubtitlesSurface = (SurfaceView) stub.inflate();
mSubtitlesSurface.setZOrderMediaOverlay(true);
mSubtitlesSurface.getHolder().setFormat(PixelFormat.TRANSLUCENT);
}
mVideoView = mVideoSurface;
}
else
{
ViewStub stub = (ViewStub) findViewById(R.id.texture_stub);
mVideoTexture = (TextureView) stub.inflate();
mVideoView = mVideoTexture;
}
}
#Override
protected void onDestroy() {
super.onDestroy();
mMediaPlayer.release();
mLibVLC.release();
}
#Override
protected void onStart() {
super.onStart();
final IVLCVout vlcVout = mMediaPlayer.getVLCVout();
if (mVideoSurface != null) {
vlcVout.setVideoView(mVideoSurface);
if (mSubtitlesSurface != null)
vlcVout.setSubtitlesView(mSubtitlesSurface);
}
else
vlcVout.setVideoView(mVideoTexture);
vlcVout.attachViews(this);
Media media = new Media(mLibVLC, Uri.parse(SAMPLE_URL));
mMediaPlayer.setMedia(media);
media.release();
mMediaPlayer.play();
if (mOnLayoutChangeListener == null) {
mOnLayoutChangeListener = new View.OnLayoutChangeListener() {
private final Runnable mRunnable = new Runnable() {
#Override
public void run() {
updateVideoSurfaces();
}
};
#Override
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
mHandler.removeCallbacks(mRunnable);
mHandler.post(mRunnable);
}
}
};
}
mVideoSurfaceFrame.addOnLayoutChangeListener(mOnLayoutChangeListener);
}
#Override
protected void onStop() {
super.onStop();
if (mOnLayoutChangeListener != null) {
mVideoSurfaceFrame.removeOnLayoutChangeListener(mOnLayoutChangeListener);
mOnLayoutChangeListener = null;
}
mMediaPlayer.stop();
mMediaPlayer.getVLCVout().detachViews();
}
private void changeMediaPlayerLayout(int displayW, int displayH) {
/* Change the video placement using the MediaPlayer API */
switch (CURRENT_SIZE) {
case SURFACE_BEST_FIT:
mMediaPlayer.setAspectRatio(null);
mMediaPlayer.setScale(0);
break;
case SURFACE_FIT_SCREEN:
case SURFACE_FILL: {
Media.VideoTrack vtrack = mMediaPlayer.getCurrentVideoTrack();
if (vtrack == null)
return;
final boolean videoSwapped = vtrack.orientation == Media.VideoTrack.Orientation.LeftBottom
|| vtrack.orientation == Media.VideoTrack.Orientation.RightTop;
if (CURRENT_SIZE == SURFACE_FIT_SCREEN) {
int videoW = vtrack.width;
int videoH = vtrack.height;
if (videoSwapped) {
int swap = videoW;
videoW = videoH;
videoH = swap;
}
if (vtrack.sarNum != vtrack.sarDen)
videoW = videoW * vtrack.sarNum / vtrack.sarDen;
float ar = videoW / (float) videoH;
float dar = displayW / (float) displayH;
float scale;
if (dar >= ar)
scale = displayW / (float) videoW; /* horizontal */
else
scale = displayH / (float) videoH; /* vertical */
mMediaPlayer.setScale(scale);
mMediaPlayer.setAspectRatio(null);
} else {
mMediaPlayer.setScale(0);
mMediaPlayer.setAspectRatio(!videoSwapped ? ""+displayW+":"+displayH
: ""+displayH+":"+displayW);
}
break;
}
case SURFACE_16_9:
mMediaPlayer.setAspectRatio("16:9");
mMediaPlayer.setScale(0);
break;
case SURFACE_4_3:
mMediaPlayer.setAspectRatio("4:3");
mMediaPlayer.setScale(0);
break;
case SURFACE_ORIGINAL:
mMediaPlayer.setAspectRatio(null);
mMediaPlayer.setScale(1);
break;
}
}
private void updateVideoSurfaces() {
int sw = getWindow().getDecorView().getWidth();
int sh = getWindow().getDecorView().getHeight();
// sanity check
if (sw * sh == 0) {
Log.e(TAG, "Invalid surface size");
return;
}
mMediaPlayer.getVLCVout().setWindowSize(sw, sh);
ViewGroup.LayoutParams lp = mVideoView.getLayoutParams();
if (mVideoWidth * mVideoHeight == 0) {
/* Case of OpenGL vouts: handles the placement of the video using MediaPlayer API */
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
mVideoView.setLayoutParams(lp);
lp = mVideoSurfaceFrame.getLayoutParams();
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
mVideoSurfaceFrame.setLayoutParams(lp);
changeMediaPlayerLayout(sw, sh);
return;
}
if (lp.width == lp.height && lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
/* We handle the placement of the video using Android View LayoutParams */
mMediaPlayer.setAspectRatio(null);
mMediaPlayer.setScale(0);
}
double dw = sw, dh = sh;
final boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
if (sw > sh && isPortrait || sw < sh && !isPortrait) {
dw = sh;
dh = sw;
}
// compute the aspect ratio
double ar, vw;
if (mVideoSarDen == mVideoSarNum) {
/* No indication about the density, assuming 1:1 */
vw = mVideoVisibleWidth;
ar = (double)mVideoVisibleWidth / (double)mVideoVisibleHeight;
} else {
/* Use the specified aspect ratio */
vw = mVideoVisibleWidth * (double)mVideoSarNum / mVideoSarDen;
ar = vw / mVideoVisibleHeight;
}
// compute the display aspect ratio
double dar = dw / dh;
switch (CURRENT_SIZE) {
case SURFACE_BEST_FIT:
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_FIT_SCREEN:
if (dar >= ar)
dh = dw / ar; /* horizontal */
else
dw = dh * ar; /* vertical */
break;
case SURFACE_FILL:
break;
case SURFACE_16_9:
ar = 16.0 / 9.0;
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_4_3:
ar = 4.0 / 3.0;
if (dar < ar)
dh = dw / ar;
else
dw = dh * ar;
break;
case SURFACE_ORIGINAL:
dh = mVideoVisibleHeight;
dw = vw;
break;
}
// set display size
lp.width = (int) Math.ceil(dw * mVideoWidth / mVideoVisibleWidth);
lp.height = (int) Math.ceil(dh * mVideoHeight / mVideoVisibleHeight);
mVideoView.setLayoutParams(lp);
if (mSubtitlesSurface != null)
mSubtitlesSurface.setLayoutParams(lp);
// set frame size (crop if necessary)
lp = mVideoSurfaceFrame.getLayoutParams();
lp.width = (int) Math.floor(dw);
lp.height = (int) Math.floor(dh);
mVideoSurfaceFrame.setLayoutParams(lp);
mVideoView.invalidate();
if (mSubtitlesSurface != null)
mSubtitlesSurface.invalidate();
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
#Override
public void onNewVideoLayout(IVLCVout vlcVout, int width, int height, int visibleWidth, int visibleHeight, int sarNum, int sarDen) {
mVideoWidth = width;
mVideoHeight = height;
mVideoVisibleWidth = visibleWidth;
mVideoVisibleHeight = visibleHeight;
mVideoSarNum = sarNum;
mVideoSarDen = sarDen;
updateVideoSurfaces();
}
}
This is how it's looking right now:
And this is how I'd like it to show:
I've photoshopped the second one btw.
Any help is appreciated. If you need any more data, just let me know.
I followed along with the same sample and ran into the same issues. It turns out you need to add an argument when initializing VLC.
final ArrayList<String> args = new ArrayList<>();
args.add("--vout=android-display"); // Add this line!
args.add("-vvv");
mLibVLC = new LibVLC(this, args);
mMediaPlayer = new MediaPlayer(mLibVLC);
Credit goes to Alexander Ukhov in the videolan forums for pointing this out.
Here ( https://drive.google.com/file/d/1lK_aOOYaKwMxvtpyyEoDXEFWcrDjGMyy/view?usp=sharing ) is the demo source code for RTMP and RTSP both. I have personally checked it and it works. I used it for live video uploading to the server. It upload video as you shoot it. Down streaming need to be done by Back end developer, they will just provide you a link and you need to use that for down streaming .

Camera Null Pointer Exception

I am new to Android. Trying to make a BarCode reader application. Getting the following error. Can anyone help?
My application stops responding on start.
09-10 15:11:52.987 26982-26982/com.aaa.Prototype E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.NullPointerException
at com.aaa.Prototype.CameraManager.setCameraDisplayOrientation(CameraManager.java:231)
at com.aaa.Prototype.CameraPreviewView.surfaceCreated(CameraPreviewView.java:54)
at android.view.SurfaceView.updateWindow(SurfaceView.java:622)
at android.view.SurfaceView.access$000(SurfaceView.java:90)
at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:185)
at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:680)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2217)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1211)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5039)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:776)
at android.view.Choreographer.doCallbacks(Choreographer.java:579)
at android.view.Choreographer.doFrame(Choreographer.java:548)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:762)
at android.os.Handler.handleCallback(Handler.java:800)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5433)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:924)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:691)
at dalvik.system.NativeStart.main(Native Method)
09-10 15:11:53.592 26982-27015/com.aaa.Prototype D/dalvikvm﹕ threadid=11: interp stack at 0x5e2e6000
Here is my code:
public class CameraManager {
/**
* Fraction of bounds size in view
*/
private static final double BOUNDS_FRACTION = 0.6;
/**
* Fraction of height of bounds in view
*/
private static final double VERTICAL_HEIGHT_FRACTION = 0.3;
/**
* Camera instance
*/
private Camera camera;
/**
* Id of camera instance
*/
private int cameraId;
/**
* Current orientation of camera
* Possible values : 0, 90, 180, 270
*/
private int orientation;
public CameraManager() {
this.camera = getCameraInstance();
}
/**
* Getter for camera
*
* #return camera instance, if it has been initialized
*/
public Camera getCamera() {
return camera;
}
/**
* Starts preview of camera, if it has been initialized
*/
public synchronized void startPreview() {
if (camera != null) {
camera.startPreview();
}
}
/**
* Stops preview of camera, if it has been initialized
*/
public synchronized void stopPreview() {
if (camera != null) {
camera.stopPreview();
}
}
/**
* Release camera, if it has been initialized
*/
public synchronized void release() {
if (camera != null) {
camera.release();
camera = null;
}
}
/**
* #return if camera has been initialized<br/>( <code>camera != null</code> )
*/
public synchronized boolean hasCamera() {
return camera != null;
}
/**
* #return bounding rect for ui
*/
public final synchronized Rect getBoundingRectUi(int uiWidth, int uiHeight) {
double heightFraction = BOUNDS_FRACTION;
double widthFraction = BOUNDS_FRACTION;
if (orientation == 90 || orientation == 270) {
heightFraction = VERTICAL_HEIGHT_FRACTION;
}
int height = (int) (uiHeight * heightFraction);
int width = (int) (uiWidth * widthFraction);
int left = (int) (uiWidth * ((1 - widthFraction) / 2));
int top = (int) (uiHeight * ((1 - heightFraction) / 2));
int right = left + width;
int bottom = top + height;
return new Rect(left, top, right, bottom);
}
/**
* #return bounding rect for camera
*/
public final synchronized Rect getBoundingRect() {
if (camera != null) {
Camera.Size previewSize = camera.getParameters().getPreviewSize();
int previewHeight = previewSize.height;
int previewWidth = previewSize.width;
double heightFraction = BOUNDS_FRACTION;
double widthFraction = BOUNDS_FRACTION;
if (orientation == 90 || orientation == 270) {
widthFraction = VERTICAL_HEIGHT_FRACTION;
}
int height = (int) (previewHeight * heightFraction);
int width = (int) (previewWidth * widthFraction);
int left = (int) (previewWidth * ((1 - widthFraction) / 2));
int top = (int) (previewHeight * ((1 - heightFraction) / 2));
int right = left + width;
int bottom = top + height;
return new Rect(left, top, right, bottom);
}
return null;
}
/**
* executes <br/> <code>camera.setOneShotPreviewCallback(callback)</code> if <br/>
* <code>camera != null</code>
* #param callback callback to provide
*/
public synchronized void requestNextFrame(Camera.PreviewCallback callback) {
if (camera != null) {
camera.setOneShotPreviewCallback(callback);
}
}
/**
* A factory method to build the appropriate LuminanceSource object based on the format
* of the preview buffers, as described by Camera.Parameters.
*
* #param data A preview frame.
* #param width The width of the image.
* #param height The height of the image.
* #return A PlanarYUVLuminanceSource instance.
*/
public synchronized PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height, Rect boundingRect) {
switch (orientation) {
case 0:
//data = flip(data);
break;
case 90:
rotate90(data, width, height);
return new PlanarYUVLuminanceSource(data, height, width, boundingRect.top, boundingRect.left,
boundingRect.height(), boundingRect.width(), false);
case 180:
break;
case 270:
rotate90(data, width, height);
break;
}
return new PlanarYUVLuminanceSource(data, width, height, boundingRect.left, boundingRect.top,
boundingRect.width(), boundingRect.height(), false);
}
/**
* Rotates image data
* #param data raw image data
* #param width width of image
* #param height height of image
*/
public void rotate90(byte[] data, int width, int height) {
int length = height * width;
int lengthDec = length - 1;
int i = 0;
do {
int k = (i * height) % lengthDec;
while (k > i) k = (height * k) % lengthDec;
if (k != i) swap(data, k, i);
} while (++i <= (length - 2));
}
/**
* Sets camera display orientation depending on current activity orientation
* #param activity activity, which holds camera preview
*/
public void setCameraDisplayOrientation(Activity activity) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
orientation = result;
camera.setDisplayOrientation(result);
}
/**
* A safe way to get an instance of the Camera object.
*/
private Camera getCameraInstance() {
Camera c = null;
try {
cameraId = 0;
c = Camera.open(); // attempt to get a Camera instance
Camera.Parameters p = c.getParameters();
p.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
c.setParameters(p);
} catch (Exception e) {
Log.e(CameraManager.class.getSimpleName(), "Camera error", e);
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
Check if you didn't forget to add the camera permission in manifest.
uses-permission android:name="android.permission.CAMERA"

Zxing Camera in Portrait mode on Android

I want to show portrait orientation on Zxing's camera.
How can this be done?
Here's how it works.
Step 1: Add following lines to rotate data before buildLuminanceSource(..) in decode(byte[] data, int width, int height)
DecodeHandler.java:
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotatedData[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width;
width = height;
height = tmp;
PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(rotatedData, width, height);
Step 2: Modify getFramingRectInPreview().
CameraManager.java
rect.left = rect.left * cameraResolution.y / screenResolution.x;
rect.right = rect.right * cameraResolution.y / screenResolution.x;
rect.top = rect.top * cameraResolution.x / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
Step 3: Disable the check for Landscape Mode in initFromCameraParameters(...)
CameraConfigurationManager.java
//remove the following
if (width < height) {
Log.i(TAG, "Display reports portrait orientation; assuming this is incorrect");
int temp = width;
width = height;
height = temp;
}
Step 4: Add following line to rotate camera insetDesiredCameraParameters(...)
CameraConfigurationManager.java
camera.setDisplayOrientation(90);
Step 5: Do not forget to set orientation of activity to portrait. I.e: manifest
To support all orientation and change automatically when rotating the activity do this, all you have to modify is the CameraManager.javaclass.
And remove this method getCurrentOrientation() from CaptureActivity.java
In CameraManager.java Create this variable:
int resultOrientation;
Add this to the openDriver(..) method:
setCameraDisplayOrientation(context, Camera.CameraInfo.CAMERA_FACING_BACK, theCamera);//this can be set after camera.setPreviewDisplay(); in api13+.
****Create this method****
Link: http://developer.android.com/reference/android/hardware/Camera.html
public static void setCameraDisplayOrientation(Context context,int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
Display display = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int degrees = 0;
switch (display.getRotation()) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
resultOrientation = (info.orientation + degrees) % 360;
resultOrientation = (360 - resultOrientation) % 360; // compensate the mirror
} else { // back-facing
resultOrientation = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(resultOrientation);
}
****Now modify getFramingRectInPreview()****
if(resultOrientation == 180 || resultOrientation == 0){//to work with landScape and reverse landScape
rect.left = rect.left * cameraResolution.x / screenResolution.x;
rect.right = rect.right * cameraResolution.x / screenResolution.x;
rect.top = rect.top * cameraResolution.y / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
}else{
rect.left = rect.left * cameraResolution.y / screenResolution.x;
rect.right = rect.right * cameraResolution.y / screenResolution.x;
rect.top = rect.top * cameraResolution.x / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
}
And modify this method public PlanarYUVLuminanceSource buildLuminanceSource(..)
if(resultOrientation == 180 || resultOrientation == 0){//TODO: This is to use camera in landScape mode
// Go ahead and assume it's YUV rather than die.
return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, rect.width(), rect.height(), false);
}else{
byte[] rotatedData = new byte[data.length];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
rotatedData[x * height + height - y - 1] = data[x + y * width];
}
int tmp = width;
width = height;
height = tmp;
return new PlanarYUVLuminanceSource(rotatedData, width, height, rect.left, rect.top, rect.width(), rect.height(), false);
}
You can use my fork of zxlib https://github.com/rusfearuth/zxing-lib-without-landscape-only. I disabled landscape mode only. You can set landscape/portrait and see correct camera view.
Adding camera.setDisplayOrientation(90); in CameraConfigurationManager.java worked for me.
for zxing 3.0, working lib https://github.com/xiaowei4895/zxing-android-portrait
for portrait mode
Thank you
I think the best library only solution is this one ...
https://github.com/SudarAbisheck/ZXing-Orient
You can include it in build.gradle as a dependency of your project in maven format ...
dependencies {
compile ''me.sudar:zxing-orient:2.1.1#aar''
}
Create AnyOrientationCaptureActivity and then override default CaptureActivity then it will work.
public void scanCode() {
IntentIntegrator integrator = new IntentIntegrator(this);
integrator.setDesiredBarcodeFormats(CommonUtil.POSEIDON_CODE_TYPES);
integrator.setPrompt("Scan");
integrator.setCameraId(0);
integrator.setBeepEnabled(false);
integrator.setBarcodeImageEnabled(false);
integrator.setOrientationLocked(false);
//Override here
integrator.setCaptureActivity(AnyOrientationCaptureActivity.class);
integrator.initiateScan();
}
//create AnyOrientationCaptureActivity extend CaptureActivity
public class AnyOrientationCaptureActivity extends CaptureActivity {
}
Define in manifest
<activity
android:name=".views.AnyOrientationCaptureActivity"
android:screenOrientation="fullSensor"
android:stateNotNeeded="true"
android:theme="#style/zxing_CaptureTheme"
android:windowSoftInputMode="stateAlwaysHidden"></activity>
This is supposed to be a synched version to the above solution
https://github.com/zxing/zxing/tree/4b124b109d90ac2960078ce68e15a39885fc1b5b
Additionally to #roylee's modification I had to apply the following to the CameraConfigurationManager.java in order to get best possible preview and QR code recognition quality
diff --git a/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java b/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java
index cd9d0d8..4f12c8c 100644
--- a/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java
+++ b/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java
## -56,21 +56,24 ## public final class CameraConfigurationManager {
Display display = manager.getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
- // We're landscape-only, and have apparently seen issues with display thinking it's portrait
+ // We're landscape-only, and have apparently seen issues with display thinking it's portrait
// when waking from sleep. If it's not landscape, assume it's mistaken and reverse them:
+ /*
if (width < height) {
Log.i(TAG, "Display reports portrait orientation; assuming this is incorrect");
int temp = width;
width = height;
height = temp;
}
+ */
screenResolution = new Point(width, height);
Log.i(TAG, "Screen resolution: " + screenResolution);
- cameraResolution = findBestPreviewSizeValue(parameters, screenResolution, false);
+ cameraResolution = findBestPreviewSizeValue(parameters, screenResolution, true);//
Log.i(TAG, "Camera resolution: " + cameraResolution);
}
void setDesiredCameraParameters(Camera camera) {
+ camera.setDisplayOrientation(90);
Camera.Parameters parameters = camera.getParameters();
if (parameters == null) {
## -99,7 +102,7 ## public final class CameraConfigurationManager {
Point getScreenResolution() {
return screenResolution;
}
-
+
public void setFrontCamera(boolean newSetting) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean currentSetting = prefs.getBoolean(PreferencesActivity.KEY_FRONT_CAMERA, false);
## -109,12 +112,12 ## public final class CameraConfigurationManager {
editor.commit();
}
}
-
+
public boolean getFrontCamera() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean(PreferencesActivity.KEY_FRONT_CAMERA, false);
}
-
+
public boolean getTorch() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false);
## -181,7 +184,14 ## public final class CameraConfigurationManager {
Camera.Size defaultSize = parameters.getPreviewSize();
bestSize = new Point(defaultSize.width, defaultSize.height);
}
+
+ // FIXME: test the bestSize == null case!
+ // swap width and height in portrait case back again
+ if (portrait) {
+ bestSize = new Point(bestSize.y, bestSize.x);
+ }
return bestSize;
+
}
private static String findSettableValue(Collection<String> supportedValues,

Categories

Resources