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