video

advertisement
Cosc 5/4730
Android media Part 2:
Pictures and Video
Emulators and Samsung
• The emulators all have problems, much of the
following code has been tested on the actual
phones
• Video playback code works, but the video may
not always display in the emulators
• android
– Video/picture capture does not work as documented.
• Samsung hardware!
– Some Samsung devices have many problems.
Something about their hardware… search for the
video and samsung for some possible answers.
Android android.media
PLAY MEDIA
Supported formats
• In general Android’s support is consistent with other
mobile phones.
• It supports the 3GP (.3gp) and MPEG-4 (.mp4) file
formats.
– 3GP is a video standard derived from MPEG-4 specifically
for use by mobile devices.
• As far as codecs go, Android supports H.263, a codec
designed for low-latency and low-bitrate
videoconferencing applications. H.263 video is
supported in either MPEG-4 (.mp4) or 3GP (.3gp) files.
Android also supports MPEG-4 Simple Profile in 3GP
files (.3gp) as well as H.264.
Android
• First method
– For greater flexibility you will need to use the mediaPlayer
and a surfaceView (or TextureView API 14+)
• MediaPlayer like the audio and use a surfaceView to display the
video.
• There are examples in the API demo, plus several of the books.
• The second method uses a VideoView to display.
– The VideoView uses a MediaController to start and stop
and provide functions to control the video play back.
– With a MediaPlayer like the audio. prepare(), then start()
– This is one I’ll cover in this lecture.
VideoView
• VideoView is a View that has video playback
capabilities and can be used directly in a
layout.
• We can then add controls (play, pause,
forward, back, etc) with the MediaController.
ViewView example
• Get the ViewView out of the layout
vv = (VideoView) this.findViewById(
R.id.VideoView);
• Setup where the file to play is
Uri videoUri =
Uri.parse(Environment.getExternalStorageDirectory
().getPath() + "/example.mp4");
vv.setVideoURI(videoUri);
• play the video
vv.start();
Adding media controllers.
• Very simple
vv = (VideoView) this.findViewById(R.id.VideoView);
vv.setMediaController(new MediaController(this));
• Now media controls will show up on the
screen.
Using native media player
• Call for an Intent and send it.
Uri data = Uri.parse(VideoFile);
intent.setDataAndType(data, "video/mp4");
startActivity(intent);
• Remember, your app is now in the
background.
Example code
• The VideoPlay example code
– This will play a video from the internet.
– If you can uncomment the code to have it play the
video from the sdcard, but you will need to copy
the file to the sdcard.
DISPLAYING A PICTURE
Displaying Pictures
– See code already covered to how display pictures.
• ImageView for example…
Android
TAKING A PICTURE
Camera vs Camera2 API
• Starting in API 21 (Lollipop) there is a new set
of APIs.
– There is no backward compatibly either.
• So first we look at Camera v1 APIs
• And then Camera2 APIs
– These are more flexible and should allow for
“filters” to be added on the fly to image.
• Finally, you can just use an intent have the
native camera app take the picture.
Taking A Picture
CAMERA V1 APIS
What to use
• Android packages
• import android.hardware.CameraDevice;
• import android.hardware.CameraDevice.CaptureParams;
– Permissions and features
<uses-permission
android:name="android.permission.CAMERA" />
– Note, This one will requirement permission checking in API 23+
• This too, if you change the how the camera is functioning.
<uses-feature android:name="android.hardware.camera" />
<uses-feature
android:name="android.hardware.camera.autofocus" />
Taking a picture
• In brief
– CameraDevice cameraDevice =
CameraDevice.open()
– To preview you need a surfaceHolder then use
setPreviewDisplay(surfaceHolder) and
cameraDevice.startPreview()
• Or TextureView if it’s API 14+
– Finally use the takePicture(…) to get the picture
– release() and close() the CameraDevice to release
it.
“View Finder”
• The view finder is implemented via a SurfaceHolder and SurfaceView
– In the layout, the a surfaceView is used.
• Example:
public class PicCapture extends Activity implements OnClickListener,
SurfaceHolder.Callback, Camera.PictureCallback {
…
cameraView = (SurfaceView) this.findViewById(R.id.CameraView);
surfaceHolder = cameraView.getHolder();
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceHolder.addCallback(this);
• To finally take the picture we need all this too.
cameraView.setFocusable(true);
cameraView.setFocusableInTouchMode(true);
cameraView.setClickable(true);
cameraView.setOnClickListener(this);
“View Finder” (2)
• The code to implement the surfaceHolder can be very simple.
• When created open the camera and set the display
surfaceCreated() {
camera = Camera.open();
try { camera.setPreviewDisplay(holder);
catch (IOException exception) {
camera.release(); }
}
• Once it’s ready, start the preview
surfaceChanged() {
camera.startPreview();
}
“View Finder” (3)
• When we are done
surfaceDestroyed() {
camera.stopPreview();
camera.release();
}
Get the Picture
• Using the Camera.PictureCallBack we implement,
we can get the data for the picture and decide
what to do it with it.
• In its simplest form, which doesn’t nothing with
the picture
public void onPictureTaken(byte[] data, Camera
camera) {
• byte[] data is the picture that was taken
• this just restarts the preview.
camera.startPreview();
}
Get the Picture (2)
• To take the picture we use the
• camera.takePicture method in the onClick method for the SurfaceView
public void onClick(View v) {
camera.takePicture(null, null, null, this);
}
• takePicture (Camera.ShutterCallback shutter, Camera.PictureCallback raw,
Camera.PictureCallback postview , Camera.PictureCallback jpeg)
• We only need the last to get the picture and it show on the previous slide.
– shutter
• the callback for image capture moment, or null
– raw
•
the callback for raw (uncompressed) image data, or null
– postview
• the callback with postview image data, may be null
– jpeg
• the callback for JPEG image data, or null
Taking A Picture
CAMERA V2 APIS
Camera2 APIs
• Like v1, this breaks up into 2 major pieces
– Viewfinder
• Connect it to a SurfaceView or TextureView.
– Taking the picture
Camera2 ViewFinder
• First find the “id” for the camera to use
– CameraManager manager = (CameraManager)
getActivity().getSystemService(Context.CAMERA_SERVICE);
– String cameraId = manager.getCameraIdList()[0];
• In this case just use the first one (tends to be the back camera)
• Then it gets complex, because we open the camera via
this
– manager.openCamera (String cameraId,
CameraDevice.StateCallback callback, Handler handler)
• StateCallback is a listener “first” and the handler is this case is
likely null.
– The handler is the thread to use, null for main. Another thread is likely
where you would apply a filter to the image, before it is displayed.
» Note, someone want to figure this out?
Camera2 ViewFinder (2)
• The call back is pretty simple, but the complex code is here.
CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
mCameraDevice = camera;
//We now have the camera, so setup the capture of the current surface.
startPreview(); //in a method since it will be called several times.
}
@Override
public void onDisconnected(CameraDevice camera) {
Log.e(TAG, "onDisconnected");
}
@Override
public void onError(CameraDevice camera, int error) {
Log.e(TAG, "onError");
}
};
startPreview
• First we need a surface (from the SurfaceView or Textview).
We are using a surfaceView
Surface surface = mHolder.getSurface();
– mHolder is our surfaceview
• mPreviewBuilder = mCameraDevice.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW);
mPreviewBuilder.addTarget(surface);
– Note, if wanted to display it on more then 1 surfaceview, we
then add the rest here. (I think).
• mCameraDevice.createCaptureSession(Arrays.asList(surfac
e), CameraCaptureStateCallback);
– The CameraCapture is listed on the next slide.
startPreview(2)
•
CameraCaptureSession.StateCallback CameraCaptureStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
mPreviewSession = session;
//now setup the update preview.
mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
HandlerThread thread = new HandlerThread("CameraPreview"); //this time we need a thread.
thread.start();
Handler backgroundHandler = new Handler(thread.getLooper());
mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, backgroundHandler);
// Request endlessly repeating capture of images by this session and place it in the surfaceview
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
Toast.makeText(context, "onConfigureFailed", Toast.LENGTH_LONG).show();
}
}, null); //null again is for a handler we don’t need.
Now we have preview
• To take a picture, it has a very similar method
but we start with the createCaptureSession
method.
– We need to setup an ImageReader, so we can save
it as JPEG (or other format), another handler,
Surfaces, plus another captureBuilder
Taking the picture
• First setup some variables we need
reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
–
Width and height handled in the code, but not shown here.
outputSurfaces = new ArrayList<Surface>(2);
outputSurfaces.add(reader.getSurface()); //here the surface is a reader, which will save to a file later on.
–
Again add more surfaces here as needed.
• Setup our handler threads here.
HandlerThread thread = new HandlerThread("CameraPicture");
thread.start();
backgroudHandler = new Handler(thread.getLooper());
• Readlistener is shown later.
reader.setOnImageAvailableListener(readerListener, backgroudHandler); //this is where the save is.
• configure the catureBuilder, which you will build, in the listener code, later on.
captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(reader.getSurface());
captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
• Fix Orientation, getJpegOreientation is shown in the code, but not here. The build for capture is in a
listener.
int deviceorientation = context.getResources().getConfiguration().orientation;
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getJpegOrientation(characteristics,
deviceorientation));
readerListener
•
A new image is available, ie the capture, so
save file.
ImageReader.OnImageAvailableListener
readerListener = new
ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader
reader) {
Image image = null;
image = reader.acquireLatestImage();
ByteBuffer buffer =
image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
save(bytes);
}
•
This is just a helper method to save the file
which at this point is just an array of bytes.
private void save(byte[] bytes) throws
IOException {
OutputStream output = null;
try {
output = new FileOutputStream(file);
output.write(bytes);
} finally {
if (null != output) {
output.close();
}
}
}
};
Taking the picture (2)
• This is the line you call to actually take the
picture
mCameraDevice.createCaptureSession(
outputSurfaces, mCaptureStateCallback,
backgroudHandler);
– Like in the preview, we need the stateCallback
• The outputSurfaces, and handler were declared 2 slides
back.
Taking the picture (3)
•
So this CameraCaptureSession.StateCallback doesn’t deal with the preview, instead is readies to save a file.
CameraCaptureSession.StateCallback mCaptureStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
session.capture(captureBuilder.build(), captureListener, backgroudHandler);
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
};
•
And the last call back
CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(CameraCaptureSession session,
CaptureRequest request, TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
startPreview(); //start the preview again.
}
};
Taking A Picture
WITH AN INTENT
Via an Intent.
• You can also have the default “camera app”
take the picture and return it to your app.
• Also doesn’t require any permissions.
Intent intent = new Intent(
android.provider.MediaStore.ACTION_IMAGE_C
APTURE);
startActivityForResult(intent, myID);
• Picture is returned in onActivityResult
Via an Intent (2)
protected void onActivityResult(int requestCode, int
resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode,
data);
//get the picture out of the intent and display in an
image view.
Bitmap bp = (Bitmap)
data.getExtras().get("data");
iv.setImageBitmap(bp);
}
Android Example Code
• A note for the example code PicCapture
– Remember it uses touch to take the picture.
– Both a V1 and v2 APIs are used in separate fragments.
• PicCapture2 uses a button and separates the
code a little better.
– Both a V1 and v2 APIs are used in separate fragments.
• PicCapture3 uses an intent to take the picture.
• CameraPreview is only camera2 APIs and it
separated up into multiple classes to make the
code easier to read.
– Similar to how it shown in the slides.
Android android.media
RECORDING VIDEO
Recording VIDEO
CAMERA V1 APIS
First…
• Most examples and code on the web and from
the android books, DO NOT work.
– lots of subtle errors
• debugging is made more difficult, because
– the camera throws errors
• CameraInput Recording is not ready … frame dropped.
– the AudioFlinger shows constant buffer overflow
warnings.
– And this is when the app is working correctly.
Uses a surfaceView
• Like a taking a picture, we need a view finder
which uses a surfaceView.
• Call for the MediaRecorder
• and setup the encoding.
– both audio and video.
Example SurfaceView
• in onCreate
recorder = new MediaRecorder();
//setup recorder settings Next Slide
SurfaceView cameraView = (SurfaceView) findViewById(R.id.CameraView);
holder = cameraView.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
• implemented methods.
public void surfaceCreated(SurfaceHolder holder) {
recorder.setPreviewDisplay(holder.getSurface());
recorder.prepare();
}
Example SurfaceView (2)
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (recording) {
recorder.stop();
recording = false;
}
recorder.release();
finish();
}
Recorder Settings
• We need to set the sources for audio and video
recorder.setAudioSource( MediaRecorder.AudioSource.MIC);
recorder.setVideoSource(
MediaRecorder.VideoSource.DEFAULT);
– MediaRecorder.VideoSource.CAMERA should work as well.
• Now we need to setup encoders.
– In android 2.2 we can use profiles instead of setting everything
manually.
– CamcorderProfile.QUALITY_HIGH or QUALITY_LOW
CamcorderProfile cpHigh =
CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
recorder.setProfile(cpHigh);
• And set the output location
recorder.setOutputFile("/sdcard/videocapture_example.mp4");
Recorder Settings (2)
• Manual settings could look like this:
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
• QUALITY_HIGH settings are MP4 file
• QUALITY_LOW settings are 3gp file
• The manual list is very long, see the android doc’s
or Apress - Pro Android Media Developing
Graphics, Music, Video, and Rich Media Apps for
Smartphones and Tablets, Chapter 11 for a full
list of settings.
Recording
• To record
recorder.start();
• To stop
recorder.stop();
– The file should be there at this point.
• Remember when you are done
recorder.release();
Example code
• The example code
– need an Sdcard to test code and the file will be located
/sdcard/videocapture_example.mp4
• The code uses an extended surfaceView call
captureSurface, instead of just a surfaceView
– The code is all there, but rearranged from the slides.
• Honesty, the code just didn’t work without an extended SurfaceView
• The code starts up with the viewfinder, touch the screen to
start recording, again to stop recording. It will then launch
the native media player to replay the video.
• As Note, using this on API23+ (marshmallow), the video is
likely upside down.
Recording VIDEO
CAMERA V2 APIS
Camera2 APIs.
• Recording video with Camera2 API is very similar
to taking a picture.
• Except…
– Change Capture to a Repeating Request.
• Why? Because we need more then one frame.
– This changes how some of the code works.
– As a note, on at least API 22 (lollipop 5.1) the example
code works once then fails.
– But works great on API 23 without any problems.
MediaRecorder again
• Setup the media recorder with a couple of extras
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setOutputFile(file.getAbsolutePath()); //file is setup else where the
example code.
mMediaRecorder.setVideoEncodingBitRate(10000000);
mMediaRecorder.setVideoFrameRate(30);
mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
• Setup and fix the orientation, hopefully. See code for getJpegOrientation.
int deviceorientation = context.getResources().getConfiguration().orientation;
mMediaRecorder.setOrientationHint(getJpegOrientation(characteristics,
deviceorientation));
mMediaRecorder.prepare();
Now the “preview” screen
• Basically the same with a couple of changes, where we add the MediaRecorder as a surface as well.
captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
outputSurfaces = new ArrayList<Surface>(2);
outputSurfaces.add(mMediaRecorder.getSurface());
outputSurfaces.add(mHolder.getSurface());
HandlerThread thread = new HandlerThread("CameraVideo");
thread.start();
backgroundHandler = new Handler(thread.getLooper());
captureBuilder.addTarget(mMediaRecorder.getSurface());
captureBuilder.addTarget(mHolder.getSurface());
captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
mCameraDevice.createCaptureSession(outputSurfaces, mCaptureStateCallback, backgroundHandler);
CaptureSession
CameraCaptureSession.StateCallback mCaptureStateCallback = new
CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
mSession = session;
• null for capture listener, because mrecoder does the work here and it’s a repeatingRequest now
session.setRepeatingRequest(captureBuilder.build(), null, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) { }
};
Recording a video
• Finally to start recording:
mMediaRecorder.start();
• And to stop
mMediaRecorder.stop();
mMediaRecorder.reset();
– The file is now stored.
Example code
• VideoCapture4 uses the camera2 and this
material covered here.
– It will check if you are running lollipop or
marshmallow.
• If lollipop, it will exit, since the app dies anyway.
• CameraPreview is an attempt to put all this
perview, picture taking and video taking
together.
– Again, works great in marshmallow and fails in
lollipop.
References
• http://developer.android.com/intl/zhCN/guide/topics/media/index.html
• http://www.brighthub.com/mobile/googleandroid/articles/43414.aspx (a difficult example to follow
and it’s for 1.6)
• Apress - Pro Android Media Developing Graphics, Music,
Video, and Rich Media Apps for Smartphones and Tablets
– Chapter 2 for taking pictures, chapter 9 for video playback.
• API examples, com.example.android.apis.media
• Camera2
– https://github.com/googlesamples/android-Camera2Video
– http://developer.android.com/reference/android/hardware/ca
mera2/package-summary.html
Q&A
Download