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