| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.example.android.basicmediadecoder; |
| |
| |
| import android.animation.TimeAnimator; |
| import android.app.Activity; |
| import android.media.MediaCodec; |
| import android.media.MediaExtractor; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.view.Menu; |
| import android.view.MenuInflater; |
| import android.view.MenuItem; |
| import android.view.Surface; |
| import android.view.TextureView; |
| import android.view.View; |
| import android.widget.TextView; |
| |
| import com.example.android.common.media.MediaCodecWrapper; |
| |
| import java.io.IOException; |
| |
| /** |
| * This activity uses a {@link android.view.TextureView} to render the frames of a video decoded using |
| * {@link android.media.MediaCodec} API. |
| */ |
| public class MainActivity extends Activity { |
| |
| private TextureView mPlaybackView; |
| private TimeAnimator mTimeAnimator = new TimeAnimator(); |
| |
| // A utility that wraps up the underlying input and output buffer processing operations |
| // into an east to use API. |
| private MediaCodecWrapper mCodecWrapper; |
| private MediaExtractor mExtractor = new MediaExtractor(); |
| TextView mAttribView = null; |
| |
| |
| /** |
| * Called when the activity is first created. |
| */ |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.sample_main); |
| mPlaybackView = (TextureView) findViewById(R.id.PlaybackView); |
| mAttribView = (TextView)findViewById(R.id.AttribView); |
| |
| } |
| |
| @Override |
| public boolean onCreateOptionsMenu(Menu menu) { |
| MenuInflater inflater = getMenuInflater(); |
| inflater.inflate(R.menu.action_menu, menu); |
| return true; |
| } |
| |
| @Override |
| protected void onPause() { |
| super.onPause(); |
| if(mTimeAnimator != null && mTimeAnimator.isRunning()) { |
| mTimeAnimator.end(); |
| } |
| |
| if (mCodecWrapper != null ) { |
| mCodecWrapper.stopAndRelease(); |
| mExtractor.release(); |
| } |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| if (item.getItemId() == R.id.menu_play) { |
| mAttribView.setVisibility(View.VISIBLE); |
| startPlayback(); |
| item.setEnabled(false); |
| } |
| return true; |
| } |
| |
| |
| public void startPlayback() { |
| |
| // Construct a URI that points to the video resource that we want to play |
| Uri videoUri = Uri.parse("android.resource://" |
| + getPackageName() + "/" |
| + R.raw.vid_bigbuckbunny); |
| |
| try { |
| |
| // BEGIN_INCLUDE(initialize_extractor) |
| mExtractor.setDataSource(this, videoUri, null); |
| int nTracks = mExtractor.getTrackCount(); |
| |
| // Begin by unselecting all of the tracks in the extractor, so we won't see |
| // any tracks that we haven't explicitly selected. |
| for (int i = 0; i < nTracks; ++i) { |
| mExtractor.unselectTrack(i); |
| } |
| |
| |
| // Find the first video track in the stream. In a real-world application |
| // it's possible that the stream would contain multiple tracks, but this |
| // sample assumes that we just want to play the first one. |
| for (int i = 0; i < nTracks; ++i) { |
| // Try to create a video codec for this track. This call will return null if the |
| // track is not a video track, or not a recognized video format. Once it returns |
| // a valid MediaCodecWrapper, we can break out of the loop. |
| mCodecWrapper = MediaCodecWrapper.fromVideoFormat(mExtractor.getTrackFormat(i), |
| new Surface(mPlaybackView.getSurfaceTexture())); |
| if (mCodecWrapper != null) { |
| mExtractor.selectTrack(i); |
| break; |
| } |
| } |
| // END_INCLUDE(initialize_extractor) |
| |
| |
| |
| |
| // By using a {@link TimeAnimator}, we can sync our media rendering commands with |
| // the system display frame rendering. The animator ticks as the {@link Choreographer} |
| // recieves VSYNC events. |
| mTimeAnimator.setTimeListener(new TimeAnimator.TimeListener() { |
| @Override |
| public void onTimeUpdate(final TimeAnimator animation, |
| final long totalTime, |
| final long deltaTime) { |
| |
| boolean isEos = ((mExtractor.getSampleFlags() & MediaCodec |
| .BUFFER_FLAG_END_OF_STREAM) == MediaCodec.BUFFER_FLAG_END_OF_STREAM); |
| |
| // BEGIN_INCLUDE(write_sample) |
| if (!isEos) { |
| // Try to submit the sample to the codec and if successful advance the |
| // extractor to the next available sample to read. |
| boolean result = mCodecWrapper.writeSample(mExtractor, false, |
| mExtractor.getSampleTime(), mExtractor.getSampleFlags()); |
| |
| if (result) { |
| // Advancing the extractor is a blocking operation and it MUST be |
| // executed outside the main thread in real applications. |
| mExtractor.advance(); |
| } |
| } |
| // END_INCLUDE(write_sample) |
| |
| // Examine the sample at the head of the queue to see if its ready to be |
| // rendered and is not zero sized End-of-Stream record. |
| MediaCodec.BufferInfo out_bufferInfo = new MediaCodec.BufferInfo(); |
| mCodecWrapper.peekSample(out_bufferInfo); |
| |
| // BEGIN_INCLUDE(render_sample) |
| if (out_bufferInfo.size <= 0 && isEos) { |
| mTimeAnimator.end(); |
| mCodecWrapper.stopAndRelease(); |
| mExtractor.release(); |
| } else if (out_bufferInfo.presentationTimeUs / 1000 < totalTime) { |
| // Pop the sample off the queue and send it to {@link Surface} |
| mCodecWrapper.popSample(true); |
| } |
| // END_INCLUDE(render_sample) |
| |
| } |
| }); |
| |
| // We're all set. Kick off the animator to process buffers and render video frames as |
| // they become available |
| mTimeAnimator.start(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |