Androidビデオアクティビティを作成しましたが、プレビュービデオが水平方向に引き伸ばされている理由を一生理解できません。(デバッグの目的で)値をハードコーディングしましたが、まだ正しくありません。レコードを押すと問題ないように見えますが、プレビュー中に引き伸ばされた状態に戻ります。
アスペクト比を手動で小さい値に設定すると(デバイスによって異なりますが、論理的な相関関係は見られません)、機能します。たとえば、SIIIで1.5に設定すると、チャームのように機能します。これは私には意味がありません。
また、surfaceCreatedが呼び出されていないことに気づきました。これが関係しているかどうかはわかりませんが、注目に値すると思いました。
似たような答えをたくさん見つけましたが、何もうまくいきませんでした。
更新:すべてのデバイスで拡張されますが、特定のデバイス(MotorolaRazrHDおよびGalaxyTab2)でレコードを押した場合にのみ修正され、他のデバイス(SamsungNoteおよびSamsungSIII)では修正されません。
これが私のコードです:
(参考までに、レコードをヒットするとカメラのNPEでクラッシュします。アプリでは問題なく動作しますが、ここに投稿する前にコードを削除する必要がありました。)
public class Video extends Activity implements SurfaceHolder.Callback
{
VideoView videoView;
MediaRecorder recorder;
Camera camera;
SurfaceHolder holder;
MediaPlayer player;
private Handler handler;
Size maxPreviewSize;
private boolean finishing;
private boolean firstRun;
private boolean isRecording;
private Object file;
public static final int MEDIA_TYPE_VIDEO = 2;
@Override
public void onCreate(Bundle savedInstanceState)
{
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.video_layout);
releaseCameraAndPreview();
videoView = (VideoView) findViewById(R.id.videoView);
}
//******************************** onPause() *************************************
/**
* Task: Releases the camera and nulls it out when the Activity is paused
*/
@Override
public void onPause()
{
super.onPause();
finishing = true;
// Releases the Media Recorder object so that it can be used the next time the app
// is launched.
releaseMediaRecorder();
// Releases the camera so that it can be used after the app is paused. Otherwise
// the camera will not be available to other apps or this app when resumed.
releaseCamera();
}
// ***************************** onDestroy() ****************************************
/**
* task: called by the system when destroying this activity. stop all
* threads, stop recording comair, explicity recycle all bitmap images from
* each row, and remove all callbacks from each view, to free up the memory.
* Request the garbage collector to run.
*/
@Override
protected void onDestroy()
{
finishing = true;
super.onDestroy();
}// end onDestroy()
//************************** onWindowFocusChanged() ********************************
/** Task: layout data cannot be accessed from onCreate, so use this method to load anything
* that has layout data.
*/
@Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
LayoutParams params = (LayoutParams) videoView.getLayoutParams();
params.width = 960;
params.height = 540;
videoView.setLayoutParams(params);
if(finishing)
{
Log.d("Video", "oWFC, finishing is true");
return;
}
firstRun = false;
// Use mCurrentCamera to select the camera desired to safely restore
// the fragment after the camera has been changed
boolean opened = safeCameraOpen();
// Install a SurfaceHolder that will give information about the VideoView
installHolder();
createPreview();
}//end onWindowFocusChanged()
//******************************** installHolder() *************************************
/**
* Task: Install a SurfaceHolder. Callback so we get notified when the
* underlying surface is created and destroyed.
*/
private void installHolder()
{
holder = videoView.getHolder();
holder.setFixedSize(960, 540);
holder.addCallback(this);
}
//*************************** surfaceCreated() ********************************
/**
* Task: Connects the camera to the Preview and starts the Preview in the VideoView
*
* @param SurfaceHolder the holder that holds the callback for the camera so that we
* know when it is stopped and started
*/
@Override
public void surfaceCreated(SurfaceHolder holder)
{
Log.d("Video", "\\\\\\\\\\\\\\\\\\\\\\\\ surfaceCreated() ////////////////////////");
}
//*************************** createPreview() ********************************
/**
* Task: Creates the preview by setting the VideoView to display the images from the
* camera.
*
* @param SurfaceHolder the holder that holds the callback for the camera so that we
* know when it is stopped and started
*/
private void createPreview()
{
try
{
// STEP 2: Connect Preview - Prepare a live camera image preview by connecting a
// SurfaceView to the camera using Camera.setPreviewDisplay().
camera.setPreviewDisplay(holder);
Rect r = holder.getSurfaceFrame();
Log.e("Video", "rectangle (holder): " + r.width() + "," + r.height());
// STEP 3: Start Preview - Call Camera.startPreview() to begin displaying the
// live camera images.
camera.startPreview();
}
catch (IOException e)
{
Log.d("Video", "Could not start the preview");
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
Log.d("Video", "\\\\\\\\\\\\\\\\\\\\\\\\ surfaceChanged() ////////////////////////");
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
Log.d("Video", "\\\\\\\\\\\\\\\\\\\\\\\\ surfaceDestroyed() ////////////////////////");
// camera.setPreviewCallback(null);
// camera.stopPreview();
// handler = null;
}
//*************************** safeCameraOpen() **********************************
/**
* Task: opens the first back-facing camera. Checks to see if the camera is open before
* attempting to open it
*
* @return Whether or not the camera was opened
*/
// TODO: choose the camera to open
private boolean safeCameraOpen()
{
Log.d("Video", "\\\\\\\\\\\\\\\\\\\\\\\\ safeCameraOpen() ////////////////////////");
boolean opened = false;
try
{
releaseCameraAndPreview();
camera = Camera.open();
opened = (camera != null);
Camera.Parameters param = camera.getParameters();
Log.e("Video", "Camera.Parameters: " + param.flatten());
List<Size> previewSize = param.getSupportedPreviewSizes();
String str = "";
maxPreviewSize = previewSize.get(0);
for(Size s:previewSize)
{
if(s.width > maxPreviewSize.width && s.width > s.height)
{
maxPreviewSize = s;
}
str += s.width + "x" + s.height+ "\t";
}
Log.e("Video", "previewSizes:\t" + str);
}
catch (Exception e)
{
Log.e(getString(R.string.app_name), "failed to open Camera");
e.printStackTrace();
}
if(opened)
Log.d("Video", "I haz camera!!!!!!!");
else
Log.d("Video", "I can haz camera??????? Noooooo!!!!");
return opened;
}
//********************* releaseCameraAndPreview() **************************
/**
* Task: releases the camera and the preview so that other apps can use the resources and to
* avoid a memory leak
*
*/
private void releaseCameraAndPreview()
{
Log.d("Video", "\\\\\\\\\\\\\\\\\\\\\\\\ releaseCameraAndPreview() ////////////////////////");
if (camera != null)
{
// Call stopPreview() to stop updating the preview surface.
camera.stopPreview();
// Important: Call release() to release the camera for use by other applications.
// Applications should release the camera immediately in onPause() (and re-open() it in
// onResume()).
camera.release();
camera = null;
}
}
//*************************** getCameraInstance() ************************************
/**
* Task: A safe way to get the instance of a Camera object
*
* @return Returns the camera or null if a camera is unavailable
*/
public Camera getCameraInstance()
{
Log.d("Video", "\\\\\\\\\\\\\\\\\\\\\\\\ getCameraInstance() ////////////////////////");
Camera c = null;
try
{
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e)
{
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
private void releaseMediaRecorder()
{
Log.d("Video", "\\\\\\\\\\\\\\\\\\\\\\\\ releaseMediaRecorder() ////////////////////////");
if (recorder != null)
{
recorder.reset(); // clear recorder configuration
recorder.release(); // release the recorder object
recorder = null;
camera.lock(); // lock camera for later use
}
}
private void releaseCamera()
{
Log.d("Video", "\\\\\\\\\\\\\\\\\\\\\\\\ releaseCamera() ////////////////////////");
if (camera != null)
{
camera.release(); // release the camera for other applications
camera = null;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
///////////////// Set up MediaRecorder ////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
private void stopRecording()
{
// stop recording and release camera
// recorder.stop(); // stop the recording
releaseMediaRecorder(); // release the MediaRecorder object
camera.lock(); // take camera access back from MediaRecorder
camera.stopPreview();
releaseCamera();
boolean opened = safeCameraOpen();
createPreview();
// inform the user that recording has stopped
isRecording = false;
}
private boolean prepareVideoRecorder()
{
Log.d("Video", "\\\\\\\\\\\\\\\\\\\\\\\\ prepareVideoRecorder() ////////////////////////");
recorder = new MediaRecorder();
recorder.setOnInfoListener(new MediaRecorder.OnInfoListener()
{
@Override
public void onInfo(MediaRecorder recorder, int what, int extra)
{
if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED)
{
Log.e("VIDEOCAPTURE","Maximum Duration Reached");
stopRecording();
}
}
});
recorder.setOnErrorListener(new MediaRecorder.OnErrorListener()
{
@Override
public void onError(MediaRecorder recorder, int what, int extra)
{
Log.e("Video", "onErrorListener\nwhat:" + what + "extra: " + extra);
}
});
// Step 1: Unlock and set camera to recorder
camera.unlock();
recorder.setCamera(camera);
// Step 2: Set sources
recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
// Step 3: Set output format and encoding (for versions prior to API Level 8)
// recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
// recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
// recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);
// recorder.setVideoSize(maxPreviewSize.width, maxPreviewSize.height);
// Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
CamcorderProfile cp;
Log.d("Video", "setProfile QUALITY_HIGH");
cp = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
Log.e("Video", "CamcorderProfile.QUALITY_HIGH: " + "cp.quality:" + cp.quality
+ ", cp.videoFrameWidth:" + cp.videoFrameHeight
+ ", cp.videoFrameWidth:" + cp.videoFrameWidth);
recorder.setProfile(cp);
recorder.setVideoSize(960, 540);
// recorder.setVideoSize(maxPreviewSize.width, maxPreviewSize.height);
// recorder.setVideoSize(cp.videoFrameWidth, cp.videoFrameHeight);
recorder.setMaxDuration(60000);
// Step 4: Set output file
// file = getOutputMediaFile(MEDIA_TYPE_VIDEO);
//
// if(file != null)
// {
// recorder.setOutputFile(file.toString());
// }
// Step 5: Set the preview output
recorder.setPreviewDisplay(videoView.getHolder().getSurface());
// Step 6: Prepare configured recorder
try
{
recorder.prepare();
}
catch (IllegalStateException e)
{
Log.d("Video", "IllegalStateException preparing recorder: " + e.getMessage());
releaseMediaRecorder();
return false;
}
catch (IOException e)
{
Log.d("Video", "IOException preparing recorder: " + e.getMessage());
releaseMediaRecorder();
return false;
}
return true;
}
private void releaseMediaPlayer()
{
player.stop();
player.release();
player = null;
}
public void onRecordClicked(View v)
{
if (isRecording)
{
Log.d("Video", "\\\\\\\\\\\\\\\\\\\\\\\\ onRecordClicked() - stop ////////////////////////");
stopRecording();
}
else
{
Log.d("Video", "\\\\\\\\\\\\\\\\\\\\\\\\ onRecordClicked() - start ////////////////////////");
// initialize video camera
if (prepareVideoRecorder())
{
Log.d("Video", "prepareVideoRecorder - true");
// Camera is available and unlocked, MediaRecorder is prepared,
// now you can start recording
recorder.start();
// inform the user that recording has started
isRecording = true;
}
else
{
// prepare didn't work, release the camera
releaseMediaRecorder();
releaseCamera();
}
}
}
}
これが私のXMLです:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/wholeDarnThing"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
tools:context=".Video" >
<VideoView
android:id="@+id/videoView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>