1/*
2 * Copyright (C) 2015 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
17package com.android.tv.tuner.exoplayer.audio;
18
19import android.os.SystemClock;
20import android.util.Log;
21import android.util.Pair;
22
23import com.google.android.exoplayer.util.MimeTypes;
24import java.util.ArrayList;
25import java.util.HashSet;
26import java.util.Set;
27
28/**
29 * Monitors the rendering position of {@link AudioTrack}.
30 */
31public class AudioTrackMonitor {
32    private static final String TAG = "AudioTrackMonitor";
33    private static final boolean DEBUG = false;
34
35    // For fetched audio samples
36    private final ArrayList<Pair<Long, Integer>> mPtsList = new ArrayList<>();
37    private final Set<Integer> mSampleSize = new HashSet<>();
38    private final Set<Integer> mCurSampleSize = new HashSet<>();
39    private final Set<Integer> mHeader = new HashSet<>();
40
41    private long mExpireMs;
42    private long mDuration;
43    private long mSampleCount;
44    private long mTotalCount;
45    private long mStartMs;
46
47    private boolean mIsMp2;
48
49    private void flush() {
50        mExpireMs += mDuration;
51        mSampleCount = 0;
52        mCurSampleSize.clear();
53        mPtsList.clear();
54    }
55
56    /**
57     * Resets and initializes {@link AudioTrackMonitor}.
58     *
59     * @param duration the frequency of monitoring in milliseconds
60     */
61    public void reset(long duration) {
62        mExpireMs = SystemClock.elapsedRealtime();
63        mDuration = duration;
64        mTotalCount = 0;
65        mStartMs = 0;
66        mSampleSize.clear();
67        mHeader.clear();
68        flush();
69    }
70
71    public void setEncoding(String mime) {
72        mIsMp2 = MimeTypes.AUDIO_MPEG_L2.equalsIgnoreCase(mime);
73    }
74
75    /**
76     * Adds an audio sample information for monitoring.
77     *
78     * @param pts the presentation timestamp of the sample
79     * @param sampleSize the size in bytes of the sample
80     * @param header the bitrate &amp; sampling information header of the sample
81     */
82    public void addPts(long pts, int sampleSize, int header) {
83        mTotalCount++;
84        mSampleCount++;
85        mSampleSize.add(sampleSize);
86        mHeader.add(header);
87        mCurSampleSize.add(sampleSize);
88        if (mTotalCount == 1) {
89            mStartMs = SystemClock.elapsedRealtime();
90        }
91        if (mPtsList.isEmpty() || mPtsList.get(mPtsList.size() - 1).first != pts) {
92            mPtsList.add(Pair.create(pts, 1));
93            return;
94        }
95        Pair<Long, Integer> pair = mPtsList.get(mPtsList.size() - 1);
96        mPtsList.set(mPtsList.size() - 1, Pair.create(pair.first, pair.second + 1));
97    }
98
99    /**
100     * Logs if interested events are present.
101     * <p>
102     * Periodic logging is not enabled in release mode in order to avoid verbose logging.
103     */
104    public void maybeLog() {
105        long now = SystemClock.elapsedRealtime();
106        if (mExpireMs != 0 && now >= mExpireMs) {
107            if (DEBUG) {
108                long unitDuration = mIsMp2 ? MpegTsDefaultAudioTrackRenderer.MP2_SAMPLE_DURATION_US
109                        : MpegTsDefaultAudioTrackRenderer.AC3_SAMPLE_DURATION_US;
110                long sampleDuration = (mTotalCount - 1) * unitDuration / 1000;
111                long totalDuration = now - mStartMs;
112                StringBuilder ptsBuilder = new StringBuilder();
113                ptsBuilder.append("PTS received ").append(mSampleCount).append(", ")
114                        .append(totalDuration - sampleDuration).append(' ');
115
116                for (Pair<Long, Integer> pair : mPtsList) {
117                    ptsBuilder.append('[').append(pair.first).append(':').append(pair.second)
118                            .append("], ");
119                }
120                Log.d(TAG, ptsBuilder.toString());
121            }
122            if (DEBUG || mCurSampleSize.size() > 1) {
123                Log.d(TAG, "PTS received sample size: "
124                        + String.valueOf(mSampleSize) + mCurSampleSize + mHeader);
125            }
126            flush();
127        }
128    }
129}
130