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