1*90c8c64dSAndroid Build Coastguard Worker /* 2*90c8c64dSAndroid Build Coastguard Worker * Copyright (C) 2013 The Android Open Source Project 3*90c8c64dSAndroid Build Coastguard Worker * 4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*90c8c64dSAndroid Build Coastguard Worker * 8*90c8c64dSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*90c8c64dSAndroid Build Coastguard Worker * 10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License. 15*90c8c64dSAndroid Build Coastguard Worker */ 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Worker package com.example.android.basicmediadecoder; 18*90c8c64dSAndroid Build Coastguard Worker 19*90c8c64dSAndroid Build Coastguard Worker 20*90c8c64dSAndroid Build Coastguard Worker import android.animation.TimeAnimator; 21*90c8c64dSAndroid Build Coastguard Worker import android.app.Activity; 22*90c8c64dSAndroid Build Coastguard Worker import android.media.MediaCodec; 23*90c8c64dSAndroid Build Coastguard Worker import android.media.MediaExtractor; 24*90c8c64dSAndroid Build Coastguard Worker import android.net.Uri; 25*90c8c64dSAndroid Build Coastguard Worker import android.os.Bundle; 26*90c8c64dSAndroid Build Coastguard Worker import android.view.Menu; 27*90c8c64dSAndroid Build Coastguard Worker import android.view.MenuInflater; 28*90c8c64dSAndroid Build Coastguard Worker import android.view.MenuItem; 29*90c8c64dSAndroid Build Coastguard Worker import android.view.Surface; 30*90c8c64dSAndroid Build Coastguard Worker import android.view.TextureView; 31*90c8c64dSAndroid Build Coastguard Worker import android.view.View; 32*90c8c64dSAndroid Build Coastguard Worker import android.widget.TextView; 33*90c8c64dSAndroid Build Coastguard Worker 34*90c8c64dSAndroid Build Coastguard Worker import com.example.android.common.media.MediaCodecWrapper; 35*90c8c64dSAndroid Build Coastguard Worker 36*90c8c64dSAndroid Build Coastguard Worker import java.io.IOException; 37*90c8c64dSAndroid Build Coastguard Worker 38*90c8c64dSAndroid Build Coastguard Worker /** 39*90c8c64dSAndroid Build Coastguard Worker * This activity uses a {@link android.view.TextureView} to render the frames of a video decoded using 40*90c8c64dSAndroid Build Coastguard Worker * {@link android.media.MediaCodec} API. 41*90c8c64dSAndroid Build Coastguard Worker */ 42*90c8c64dSAndroid Build Coastguard Worker public class MainActivity extends Activity { 43*90c8c64dSAndroid Build Coastguard Worker 44*90c8c64dSAndroid Build Coastguard Worker private TextureView mPlaybackView; 45*90c8c64dSAndroid Build Coastguard Worker private TimeAnimator mTimeAnimator = new TimeAnimator(); 46*90c8c64dSAndroid Build Coastguard Worker 47*90c8c64dSAndroid Build Coastguard Worker // A utility that wraps up the underlying input and output buffer processing operations 48*90c8c64dSAndroid Build Coastguard Worker // into an east to use API. 49*90c8c64dSAndroid Build Coastguard Worker private MediaCodecWrapper mCodecWrapper; 50*90c8c64dSAndroid Build Coastguard Worker private MediaExtractor mExtractor = new MediaExtractor(); 51*90c8c64dSAndroid Build Coastguard Worker TextView mAttribView = null; 52*90c8c64dSAndroid Build Coastguard Worker 53*90c8c64dSAndroid Build Coastguard Worker 54*90c8c64dSAndroid Build Coastguard Worker /** 55*90c8c64dSAndroid Build Coastguard Worker * Called when the activity is first created. 56*90c8c64dSAndroid Build Coastguard Worker */ 57*90c8c64dSAndroid Build Coastguard Worker @Override onCreate(Bundle savedInstanceState)58*90c8c64dSAndroid Build Coastguard Worker public void onCreate(Bundle savedInstanceState) { 59*90c8c64dSAndroid Build Coastguard Worker super.onCreate(savedInstanceState); 60*90c8c64dSAndroid Build Coastguard Worker setContentView(R.layout.sample_main); 61*90c8c64dSAndroid Build Coastguard Worker mPlaybackView = (TextureView) findViewById(R.id.PlaybackView); 62*90c8c64dSAndroid Build Coastguard Worker mAttribView = (TextView)findViewById(R.id.AttribView); 63*90c8c64dSAndroid Build Coastguard Worker 64*90c8c64dSAndroid Build Coastguard Worker } 65*90c8c64dSAndroid Build Coastguard Worker 66*90c8c64dSAndroid Build Coastguard Worker @Override onCreateOptionsMenu(Menu menu)67*90c8c64dSAndroid Build Coastguard Worker public boolean onCreateOptionsMenu(Menu menu) { 68*90c8c64dSAndroid Build Coastguard Worker MenuInflater inflater = getMenuInflater(); 69*90c8c64dSAndroid Build Coastguard Worker inflater.inflate(R.menu.action_menu, menu); 70*90c8c64dSAndroid Build Coastguard Worker return true; 71*90c8c64dSAndroid Build Coastguard Worker } 72*90c8c64dSAndroid Build Coastguard Worker 73*90c8c64dSAndroid Build Coastguard Worker @Override onPause()74*90c8c64dSAndroid Build Coastguard Worker protected void onPause() { 75*90c8c64dSAndroid Build Coastguard Worker super.onPause(); 76*90c8c64dSAndroid Build Coastguard Worker if(mTimeAnimator != null && mTimeAnimator.isRunning()) { 77*90c8c64dSAndroid Build Coastguard Worker mTimeAnimator.end(); 78*90c8c64dSAndroid Build Coastguard Worker } 79*90c8c64dSAndroid Build Coastguard Worker 80*90c8c64dSAndroid Build Coastguard Worker if (mCodecWrapper != null ) { 81*90c8c64dSAndroid Build Coastguard Worker mCodecWrapper.stopAndRelease(); 82*90c8c64dSAndroid Build Coastguard Worker mExtractor.release(); 83*90c8c64dSAndroid Build Coastguard Worker } 84*90c8c64dSAndroid Build Coastguard Worker } 85*90c8c64dSAndroid Build Coastguard Worker 86*90c8c64dSAndroid Build Coastguard Worker @Override onOptionsItemSelected(MenuItem item)87*90c8c64dSAndroid Build Coastguard Worker public boolean onOptionsItemSelected(MenuItem item) { 88*90c8c64dSAndroid Build Coastguard Worker if (item.getItemId() == R.id.menu_play) { 89*90c8c64dSAndroid Build Coastguard Worker mAttribView.setVisibility(View.VISIBLE); 90*90c8c64dSAndroid Build Coastguard Worker startPlayback(); 91*90c8c64dSAndroid Build Coastguard Worker item.setEnabled(false); 92*90c8c64dSAndroid Build Coastguard Worker } 93*90c8c64dSAndroid Build Coastguard Worker return true; 94*90c8c64dSAndroid Build Coastguard Worker } 95*90c8c64dSAndroid Build Coastguard Worker 96*90c8c64dSAndroid Build Coastguard Worker startPlayback()97*90c8c64dSAndroid Build Coastguard Worker public void startPlayback() { 98*90c8c64dSAndroid Build Coastguard Worker 99*90c8c64dSAndroid Build Coastguard Worker // Construct a URI that points to the video resource that we want to play 100*90c8c64dSAndroid Build Coastguard Worker Uri videoUri = Uri.parse("android.resource://" 101*90c8c64dSAndroid Build Coastguard Worker + getPackageName() + "/" 102*90c8c64dSAndroid Build Coastguard Worker + R.raw.vid_bigbuckbunny); 103*90c8c64dSAndroid Build Coastguard Worker 104*90c8c64dSAndroid Build Coastguard Worker try { 105*90c8c64dSAndroid Build Coastguard Worker 106*90c8c64dSAndroid Build Coastguard Worker // BEGIN_INCLUDE(initialize_extractor) 107*90c8c64dSAndroid Build Coastguard Worker mExtractor.setDataSource(this, videoUri, null); 108*90c8c64dSAndroid Build Coastguard Worker int nTracks = mExtractor.getTrackCount(); 109*90c8c64dSAndroid Build Coastguard Worker 110*90c8c64dSAndroid Build Coastguard Worker // Begin by unselecting all of the tracks in the extractor, so we won't see 111*90c8c64dSAndroid Build Coastguard Worker // any tracks that we haven't explicitly selected. 112*90c8c64dSAndroid Build Coastguard Worker for (int i = 0; i < nTracks; ++i) { 113*90c8c64dSAndroid Build Coastguard Worker mExtractor.unselectTrack(i); 114*90c8c64dSAndroid Build Coastguard Worker } 115*90c8c64dSAndroid Build Coastguard Worker 116*90c8c64dSAndroid Build Coastguard Worker 117*90c8c64dSAndroid Build Coastguard Worker // Find the first video track in the stream. In a real-world application 118*90c8c64dSAndroid Build Coastguard Worker // it's possible that the stream would contain multiple tracks, but this 119*90c8c64dSAndroid Build Coastguard Worker // sample assumes that we just want to play the first one. 120*90c8c64dSAndroid Build Coastguard Worker for (int i = 0; i < nTracks; ++i) { 121*90c8c64dSAndroid Build Coastguard Worker // Try to create a video codec for this track. This call will return null if the 122*90c8c64dSAndroid Build Coastguard Worker // track is not a video track, or not a recognized video format. Once it returns 123*90c8c64dSAndroid Build Coastguard Worker // a valid MediaCodecWrapper, we can break out of the loop. 124*90c8c64dSAndroid Build Coastguard Worker mCodecWrapper = MediaCodecWrapper.fromVideoFormat(mExtractor.getTrackFormat(i), 125*90c8c64dSAndroid Build Coastguard Worker new Surface(mPlaybackView.getSurfaceTexture())); 126*90c8c64dSAndroid Build Coastguard Worker if (mCodecWrapper != null) { 127*90c8c64dSAndroid Build Coastguard Worker mExtractor.selectTrack(i); 128*90c8c64dSAndroid Build Coastguard Worker break; 129*90c8c64dSAndroid Build Coastguard Worker } 130*90c8c64dSAndroid Build Coastguard Worker } 131*90c8c64dSAndroid Build Coastguard Worker // END_INCLUDE(initialize_extractor) 132*90c8c64dSAndroid Build Coastguard Worker 133*90c8c64dSAndroid Build Coastguard Worker 134*90c8c64dSAndroid Build Coastguard Worker 135*90c8c64dSAndroid Build Coastguard Worker 136*90c8c64dSAndroid Build Coastguard Worker // By using a {@link TimeAnimator}, we can sync our media rendering commands with 137*90c8c64dSAndroid Build Coastguard Worker // the system display frame rendering. The animator ticks as the {@link Choreographer} 138*90c8c64dSAndroid Build Coastguard Worker // recieves VSYNC events. 139*90c8c64dSAndroid Build Coastguard Worker mTimeAnimator.setTimeListener(new TimeAnimator.TimeListener() { 140*90c8c64dSAndroid Build Coastguard Worker @Override 141*90c8c64dSAndroid Build Coastguard Worker public void onTimeUpdate(final TimeAnimator animation, 142*90c8c64dSAndroid Build Coastguard Worker final long totalTime, 143*90c8c64dSAndroid Build Coastguard Worker final long deltaTime) { 144*90c8c64dSAndroid Build Coastguard Worker 145*90c8c64dSAndroid Build Coastguard Worker boolean isEos = ((mExtractor.getSampleFlags() & MediaCodec 146*90c8c64dSAndroid Build Coastguard Worker .BUFFER_FLAG_END_OF_STREAM) == MediaCodec.BUFFER_FLAG_END_OF_STREAM); 147*90c8c64dSAndroid Build Coastguard Worker 148*90c8c64dSAndroid Build Coastguard Worker // BEGIN_INCLUDE(write_sample) 149*90c8c64dSAndroid Build Coastguard Worker if (!isEos) { 150*90c8c64dSAndroid Build Coastguard Worker // Try to submit the sample to the codec and if successful advance the 151*90c8c64dSAndroid Build Coastguard Worker // extractor to the next available sample to read. 152*90c8c64dSAndroid Build Coastguard Worker boolean result = mCodecWrapper.writeSample(mExtractor, false, 153*90c8c64dSAndroid Build Coastguard Worker mExtractor.getSampleTime(), mExtractor.getSampleFlags()); 154*90c8c64dSAndroid Build Coastguard Worker 155*90c8c64dSAndroid Build Coastguard Worker if (result) { 156*90c8c64dSAndroid Build Coastguard Worker // Advancing the extractor is a blocking operation and it MUST be 157*90c8c64dSAndroid Build Coastguard Worker // executed outside the main thread in real applications. 158*90c8c64dSAndroid Build Coastguard Worker mExtractor.advance(); 159*90c8c64dSAndroid Build Coastguard Worker } 160*90c8c64dSAndroid Build Coastguard Worker } 161*90c8c64dSAndroid Build Coastguard Worker // END_INCLUDE(write_sample) 162*90c8c64dSAndroid Build Coastguard Worker 163*90c8c64dSAndroid Build Coastguard Worker // Examine the sample at the head of the queue to see if its ready to be 164*90c8c64dSAndroid Build Coastguard Worker // rendered and is not zero sized End-of-Stream record. 165*90c8c64dSAndroid Build Coastguard Worker MediaCodec.BufferInfo out_bufferInfo = new MediaCodec.BufferInfo(); 166*90c8c64dSAndroid Build Coastguard Worker mCodecWrapper.peekSample(out_bufferInfo); 167*90c8c64dSAndroid Build Coastguard Worker 168*90c8c64dSAndroid Build Coastguard Worker // BEGIN_INCLUDE(render_sample) 169*90c8c64dSAndroid Build Coastguard Worker if (out_bufferInfo.size <= 0 && isEos) { 170*90c8c64dSAndroid Build Coastguard Worker mTimeAnimator.end(); 171*90c8c64dSAndroid Build Coastguard Worker mCodecWrapper.stopAndRelease(); 172*90c8c64dSAndroid Build Coastguard Worker mExtractor.release(); 173*90c8c64dSAndroid Build Coastguard Worker } else if (out_bufferInfo.presentationTimeUs / 1000 < totalTime) { 174*90c8c64dSAndroid Build Coastguard Worker // Pop the sample off the queue and send it to {@link Surface} 175*90c8c64dSAndroid Build Coastguard Worker mCodecWrapper.popSample(true); 176*90c8c64dSAndroid Build Coastguard Worker } 177*90c8c64dSAndroid Build Coastguard Worker // END_INCLUDE(render_sample) 178*90c8c64dSAndroid Build Coastguard Worker 179*90c8c64dSAndroid Build Coastguard Worker } 180*90c8c64dSAndroid Build Coastguard Worker }); 181*90c8c64dSAndroid Build Coastguard Worker 182*90c8c64dSAndroid Build Coastguard Worker // We're all set. Kick off the animator to process buffers and render video frames as 183*90c8c64dSAndroid Build Coastguard Worker // they become available 184*90c8c64dSAndroid Build Coastguard Worker mTimeAnimator.start(); 185*90c8c64dSAndroid Build Coastguard Worker } catch (IOException e) { 186*90c8c64dSAndroid Build Coastguard Worker e.printStackTrace(); 187*90c8c64dSAndroid Build Coastguard Worker } 188*90c8c64dSAndroid Build Coastguard Worker } 189*90c8c64dSAndroid Build Coastguard Worker } 190