1 /*
2  * Copyright (C) 2021 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 android.mediapc.cts;
18 
19 import android.media.AudioAttributes;
20 import android.media.AudioFormat;
21 import android.media.AudioTrack;
22 import android.media.MediaCodec;
23 import android.media.MediaExtractor;
24 import android.media.MediaFormat;
25 
26 import java.nio.ByteBuffer;
27 
28 /**
29  * The following class decode and render the audio (will be audible), until loadStatus is finished.
30  * If input reaches eos, it will rewind the input to start position.
31  */
32 class AudioPlaybackLoad extends CodecDecoderTestBase {
33     private final String mDecoderName;
34     private final LoadStatus mLoadStatus;
35 
36     private long mBasePts;
37     private long mMaxPts;
38     private AudioTrack mTrack;
39 
AudioPlaybackLoad(String mime, String testFile, String decoderName, LoadStatus loadStatus)40     AudioPlaybackLoad(String mime, String testFile, String decoderName, LoadStatus loadStatus) {
41         super(mime, testFile);
42         mDecoderName = decoderName;
43         mLoadStatus = loadStatus;
44         mBasePts = 0;
45         mMaxPts = 0;
46     }
47 
doDecodeAndPlayback()48     public void doDecodeAndPlayback() throws Exception {
49         MediaFormat format = setUpSource(mTestFile);
50         mTrack = createAudioTrack(format);
51         mTrack.play();
52         mCodec = MediaCodec.createByCodecName(mDecoderName);
53         mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
54         configureCodec(format, false, false, false);
55         mCodec.start();
56         doWork(Integer.MAX_VALUE);
57         queueEOS();
58         waitForAllOutputs();
59         mCodec.stop();
60         mCodec.release();
61         mExtractor.release();
62         mTrack.pause();
63         mTrack.flush();
64         mTrack.release();
65     }
66 
createAudioTrack(MediaFormat format)67     private AudioTrack createAudioTrack(MediaFormat format) {
68         final int channelMask = getChannelMask(format);
69         final int encoding = AudioFormat.ENCODING_PCM_16BIT;
70         final int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
71         final AudioFormat audioFormat = new AudioFormat.Builder()
72                 .setEncoding(encoding)
73                 .setSampleRate(sampleRate)
74                 .setChannelMask(channelMask)
75                 .build();
76         final AudioAttributes audioAttributes = new AudioAttributes.Builder()
77                 .setUsage(AudioAttributes.USAGE_MEDIA)
78                 .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
79                 .build();
80         final int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelMask, encoding);
81         final int bufferSize = 2 * minBufferSize;
82         return new AudioTrack.Builder()
83                 .setBufferSizeInBytes(bufferSize)
84                 .setAudioAttributes(audioAttributes)
85                 .setAudioFormat(audioFormat)
86                 .build();
87     }
88 
getChannelMask(MediaFormat format)89     private int getChannelMask(MediaFormat format) {
90         final int count = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
91         if (count == 1) {
92             return AudioFormat.CHANNEL_OUT_MONO;
93         } else if (count == 2) {
94             return AudioFormat.CHANNEL_OUT_STEREO;
95         } else if (count == 6) {
96             return AudioFormat.CHANNEL_OUT_5POINT1;
97         }
98         return -1;
99     }
100 
101     // Enqueue input to decoder until loadStatus is finished or unexpected eos is seen.
102     @Override
enqueueInput(int bufferIndex)103     void enqueueInput(int bufferIndex) {
104         if (mExtractor.getSampleSize() < 0 || mLoadStatus.isLoadFinished()) {
105             enqueueEOS(bufferIndex);
106         } else {
107             ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
108             int size = mExtractor.readSampleData(inputBuffer, 0);
109             long pts = mExtractor.getSampleTime();
110             mMaxPts = Math.max(mMaxPts, mBasePts + pts);
111             int extractorFlags = mExtractor.getSampleFlags();
112             int codecFlags = 0;
113             if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
114                 codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
115             }
116             mCodec.queueInputBuffer(bufferIndex, 0, size, mBasePts + pts, codecFlags);
117             if (size > 0 && (codecFlags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
118                 mInputCount++;
119             }
120             // If eos is reached, seek to start position.
121             if (!mExtractor.advance()) {
122                 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
123                 mBasePts = mMaxPts + 1000000L;
124             }
125         }
126     }
127 
128     @Override
dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info)129     void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
130         if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
131             mSawOutputEOS = true;
132         }
133         if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
134             mOutputCount++;
135         }
136         final ByteBuffer buffer = mCodec.getOutputBuffer(bufferIndex);
137         final byte[] audio = new byte[info.size];
138         buffer.clear(); // prepare buffer for reading
139         buffer.get(audio);
140         mTrack.write(audio, 0, audio.length);
141         mCodec.releaseOutputBuffer(bufferIndex, false);
142     }
143 }
144