Zillow empowers home shoppers to make informed decisions by providing them with as much information as possible about each listing. Also, agents and home sellers need to be able to highlight each home's key features and layout. Moving to providing home videos, in addition to the standard photo gallery, is a natural progression.

In February 2016, Zillow launched video walkthroughs, a free feature that allows agents and home sellers to use their phones to capture and upload a two-minute video that highlights the layout and features of the home. Videos can be recorded in short segments, versus one continuous video, so that agents can show different views and shots of the home easily. Once published, listings with videos are prominently displayed in search results and on each home's detail page, giving home shoppers a new and more immersive way to envision themselves in a home.

In this post we will review the technical challenges of building video capture for the Android platform.

Video Capture

Because devices in the Android world are so diverse, we first decided to restrict video capture to certain devices. This allowed us to focus on quality as well as to minimize technical complexity. The minimum feature set and requirements we settled on were:

  • Devices need to support 720p videos
  • Devices need to have enough storage
  • Landscape only (no portrait)
  • Phone only (no tablet)

Video Recording

Although Android's deprecated Camera API and MediaRecorder are not feature rich, we still ended up using these classes. The reason is that we needed to support Android API 14+ at the time and our use cases did not dictate the need for something more complex. In the end we shipped with a SurfaceView for pre-JellyBean(JB) devices and a TextureView for JellyBean and above (because MediaRecorder pre-JB cannot be used with an output SurfaceTexture).

Preparing the CamcorderProfile

Configuring the CamcorderProfile is pretty simple. The profile is based on the 720p predefined format in the Android OS and the output video format is an mp4 with a specific bit rate.

public CamcorderProfilegetCamcorderProfile() { // If 720p recording cannot be done then camcorder profile cannot be created! if( !CamcorderProfile.hasProfile( CamcorderProfile.QUALITY_720P ) ) { return null; }mCamcorderProfile = CamcorderProfile.get( CamcorderProfile.QUALITY_720P );mCamcorderProfile.fileFormat = MediaRecorder.OutputFormat.MPEG_4;mCamcorderProfile.videoCodec = MediaRecorder.VideoEncoder.H264;mCamcorderProfile.videoBitRate =VIDEO_ENCODING_BIT_RATE; returnmCamcorderProfile; }

Preparing the Camera

The camera preparation is straightforward and there is plenty of Android documentation. Some of the key things to keep in mind are:

  • Listen to TextureView's surface listener

mPreview.setSurfaceTextureListener( this );
  • Prepare camera when surface is available and release camera when surface is destroyed.

@Override public voidonSurfaceTextureAvailable( SurfaceTexturesurface, intwidth, intheight ) {prepareCamera(); } @Override public booleanonSurfaceTextureDestroyed( SurfaceTexturesurface ) {releaseCamera(); return true; }
  • Auto focus provided by the OS for continuous video capture

Camera.Parametersparameters =mCamera.getParameters(); // Apply focus mode to continuous video if available List String>focusModes =parameters.getSupportedFocusModes(); if (focusModes.contains( Parameters.FOCUS_MODE_CONTINUOUS_VIDEO ) ) {parameters.setFocusMode( Parameters.FOCUS_MODE_CONTINUOUS_VIDEO ); } // Setup the preview sizeparameters.setPreviewSize(profile.videoFrameWidth,profile.videoFrameHeight );mCamera.setParameters(parameters );
  • Setting up the Preview Texture/ Preview Display for Android Jelly Bean(JB+) and for pre-JB respectively.

if( AndroidCompatibility.isAndroidJellyBeanOrNewer() ) {mCamera.setPreviewTexture(mPreview.getSurfaceTexture() ); } else {mCamera.setPreviewDisplay(mSurfaceHolder ); }mCamera.startPreview();
  • Make use of the camera's display orientation to show the user the correct orientation in which the frames are being recorded.

mCamera.setDisplayOrientation(NATURAL_CAMERA_ROTATION_DEGREE );
  • Do not forget to release the camera when you are done with.

mCamera.stopPreview(); // release the camera for other applicationsmCamera.release();mCamera = null;

Preparing Media Recorder

We did not face any problems preparing the media recorder. The sequence of events is simply:

  • Set camera
  • Set the video source
  • Set CamcorderProfile
  • Set the output file

The only thing to keep in mind is that the video recorder is a time consuming operation so it's best to get it done in another thread. An AsyncTask works well for this.

@Override protected BooleandoInBackground( Void...voids ) { // initialize video camera if (prepareVideoRecorder() ) { // Camera is available and unlocked, MediaRecorder is prepared, // so start recordingmMediaRecorder.start(); } else { return false; } return true; }

Join Multiple Video Segments

Users need to be able to both pause recording and restart video capture. Android does not provide an API or a mechanism to handle this. We researched multiple solutions but settled on MP4Parser.

It's a well written and stable Java library that is fast enough. As soon as a user accepts a video segment, we start to append it to an already existing video.

// Create a movie array Movie[]inMovies = new Movie[] { MovieCreator.build(filePath1 ), MovieCreator.build(filePath2 ) }; List Track>videoTracks = new LinkedList Track>(); for ( Moviem :inMovies ) { for ( Trackt :m.getTracks() ) { if (t.getHandler().equals(VIDEO_TAG ) ) {videoTracks.add(t); } } } Movieresult = new Movie(); if (videoTracks.size() > 0 ) {result.addTrack( new AppendTrack(videoTracks.toArray( new Track[videoTracks.size()] ) ) ); } Container out = new DefaultMp4Builder().build(result ); // Write out the file to disk FileChannelfc = new RandomAccessFile(outputFile, 'rw' ).getChannel(); out.writeContainer(fc );fc.close();

Extract Cover Frame

Videos need to have a cover photo. Android has provided MediaMetadataRetriever to extract and retrieve frames from a video. Once all the videos are stitched together we extract out a frame from the video. Since the extraction is based on a microsecond, we extract the first microsecond of the video. All of this work should be done in an AsyncTask since it is a time consuming operation.

// Open up the media metadata retriever MediaMetadataRetrievermediaMetadataRetriever = new MediaMetadataRetriever();mediaMetadataRetriever.setDataSource(mVideoPath ); Bitmapbitmap =mediaMetadataRetriever.getFrameAtTime(mTimeToExtractInMicros, MediaMetadataRetriever.OPTION_CLOSEST_SYNC ); // Need to release when donemediaMetadataRetriever.release();

Replaying Video

We use Exoplayer to play the video. Although the Android provided MediaPlayer would have been sufficient to play our video, we prefer to use Exoplayer because of Google's usage and support. Why Exoplayer is not provided in the Android OS could be a discussion for another day...

Our Results

Since launch, we've found that adding a video walkthrough improves home shopper engagement with each listing. Listings with video walkthroughs get more than twice the amount of pageviews and twice the amount of saves compared to listings without a video walkthrough, and adding a video walkthrough is a great way to increase the number of contacts a listing can generate.

Zillow Inc. published this content on 27 September 2016 and is solely responsible for the information contained herein.
Distributed by Public, unedited and unaltered, on 28 September 2016 00:00:12 UTC.

Original documenthttps://engineering.zillow.com/video-walkthroughs-zillow-android-app/

Public permalinkhttp://www.publicnow.com/view/794C04F43AECBA0388746654616A1803AE5D5FEA