MythTV master
audiooutputaudiotrack.cpp
Go to the documentation of this file.
1#include <QtGlobal>
2#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
3#include <QAndroidJniObject>
4#include <QAndroidJniEnvironment>
5#include <algorithm>
6#else
7#include <QJniEnvironment>
8#include <QJniObject>
9#define QAndroidJniEnvironment QJniEnvironment
10#define QAndroidJniObject QJniObject
11#endif
12#include <android/log.h>
13
16
17static constexpr uint8_t CHANNELS_MIN { 1 };
18static constexpr uint8_t CHANNELS_MAX { 8 };
19
20#define ANDROID_EXCEPTION_CHECK \
21 if (env->ExceptionCheck()) { \
22 env->ExceptionDescribe(); \
23 env->ExceptionClear(); \
24 exception=true; \
25 } else { \
26 exception=false; \
27 }
28// clear exception without checking
29#define ANDROID_EXCEPTION_CLEAR \
30 if (env->ExceptionCheck()) { \
31 env->ExceptionDescribe(); \
32 env->ExceptionClear(); \
33 }
34
35#define LOC QString("AudioTrack: ")
36
37// Constants from Android Java API
38// class android.media.AudioFormat
39enum AF : uint8_t {
48};
49
50// for debugging
51#include <android/log.h>
52
54 AudioOutputBase(settings)
55{
56 InitSettings(settings);
57 if (settings.m_init)
58 Reconfigure(settings);
59}
60
62{
63 KillAudio();
64}
65
67{
68 bool exception=false;
70 jint encoding = 0;
71 jint sampleRate = m_sampleRate;
72
73 // m_bitsPer10Frames = output bits per 10 frames
75
76 if ((m_passthru || m_enc) && m_sourceBitRate > 0)
78
79 // 50 milliseconds
81
82 m_fragmentSize = std::max(m_fragmentSize, 1536);
83
84
85 if (m_passthru || m_enc)
86 {
87 switch (m_codec)
88 {
89 case AV_CODEC_ID_AC3:
90 encoding = AF_ENCODING_AC3;
91 break;
92 case AV_CODEC_ID_DTS:
93 encoding = AF_ENCODING_DTS;
94 break;
95 case AV_CODEC_ID_EAC3:
96 encoding = AF_ENCODING_E_AC3;
97 break;
98 case AV_CODEC_ID_TRUEHD:
99 encoding = AF_ENCODING_DOLBY_TRUEHD;
100 break;
101
102 default:
103 LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + QString(" No support for audio passthru encoding %1").arg(m_codec));
104 return false;
105 }
106 }
107 else
108 {
109 switch (m_outputFormat)
110 {
111 case FORMAT_U8:
112 // This could be used to get the value from java instead // of haning these constants in pour header file.
113 // encoding = QAndroidJniObject::getStaticField<jint>
114 // ("android.media.AudioFormat","ENCODING_PCM_8BIT");
115 encoding = AF_ENCODING_PCM_8BIT;
116 break;
117 case FORMAT_S16:
118 encoding = AF_ENCODING_PCM_16BIT;
119 break;
120 case FORMAT_FLT:
121 encoding = AF_ENCODING_PCM_FLOAT;
122 break;
123 default:
124 LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + QString(" No support for audio format %1").arg(m_outputFormat));
125 return false;
126 }
127 }
128
129 jint minBufferSize = m_fragmentSize * 4;
130 m_soundcardBufferSize = minBufferSize;
131 jint channels = m_channels;
132
133 m_audioTrack = new QAndroidJniObject("org/mythtv/audio/AudioOutputAudioTrack",
134 "(IIII)V", encoding, sampleRate, minBufferSize, channels);
136
137 if (exception)
138 {
139 LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + QString(" Java Exception when creating AudioTrack"));
140 m_audioTrack = nullptr;
141 return false;
142 }
143 if (!m_passthru && !m_enc)
144 {
145 jint bitsPer10Frames = m_bitsPer10Frames;
146 m_audioTrack->callMethod<void>("setBitsPer10Frames","(I)V",bitsPer10Frames);
147 }
148 return true;
149}
150
152{
154 if (m_audioTrack)
155 {
156 m_audioTrack->callMethod<void>("release");
158 delete m_audioTrack;
159 m_audioTrack = nullptr;
160 }
161}
162
164{
165 bool exception=false;
167 jint bufsize = 0;
168
169 auto *settings = new AudioOutputSettings();
170
171 int supportedrate = 0;
172 while (int rate = settings->GetNextRate())
173 {
174 // Checking for valid rates using getMinBufferSize.
175 // See https://stackoverflow.com/questions/8043387/android-audiorecord-supported-sampling-rates/22317382
176 bufsize = QAndroidJniObject::callStaticMethod<jint>
177 ("android/media/AudioTrack", "getMinBufferSize", "(III)I",
180 if (bufsize > 0 && !exception)
181 {
182 settings->AddSupportedRate(rate);
183 // save any supported rate for later
184 supportedrate = rate;
185 }
186 }
187
188 // Checking for valid format using getMinBufferSize.
189 bufsize = QAndroidJniObject::callStaticMethod<jint>
190 ("android/media/AudioTrack", "getMinBufferSize", "(III)I",
193 if (bufsize > 0 && !exception)
194 settings->AddSupportedFormat(FORMAT_U8);
195 // 16bit always supported
196 settings->AddSupportedFormat(FORMAT_S16);
197
198 bufsize = QAndroidJniObject::callStaticMethod<jint>
199 ("android/media/AudioTrack", "getMinBufferSize", "(III)I",
202 if (bufsize > 0 && !exception)
203 settings->AddSupportedFormat(FORMAT_FLT);
204
206 {
207 settings->AddSupportedChannels(channels);
208 }
209 settings->setPassthrough(0);
210
211 return settings;
212}
213
214void AudioOutputAudioTrack::WriteAudio(unsigned char* aubuf, int size)
215{
216 bool exception=false;
219 {
220 if (m_audioTrack)
221 {
222 jboolean param = true;
223 m_audioTrack->callMethod<void>("pause","(Z)V",param);
225 }
226 return;
227 }
228 // create a java byte array
229 jbyteArray arr = env->NewByteArray(size);
230 env->SetByteArrayRegion(arr, 0, size, reinterpret_cast<jbyte*>(aubuf));
231 jint ret = -99;
232 if (m_audioTrack)
233 {
234 ret = m_audioTrack->callMethod<jint>("write","([BI)I", arr, size);
236 }
237 env->DeleteLocalRef(arr);
238 if (ret != size || exception)
239 LOG(VB_GENERAL, LOG_ERR, LOC + __func__
240 + QString(" Audio Write failed, size %1 return %2 exception %3")
241 .arg(size).arg(ret).arg(exception));
242
243 LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO, LOC + __func__
244 + QString(" WriteAudio size=%1 written=%2")
245 .arg(size).arg(ret));
246}
247
248
250{
252 int buffered (0);
253 if (m_audioTrack)
254 {
255 // This may return a negative value, because there
256 // is data already played that is still in the "Audio circular buffer"
257 buffered
258 = m_audioTrack->callMethod<jint>("getBufferedBytes");
259 bool exception=false;
261 if (exception)
262 buffered = 0;
263 int latency
264 = m_audioTrack->callMethod<jint>("getLatencyViaHeadPosition");
266 if (exception)
267 latency = 0;
268 buffered += latency * m_sampleRate / 1000 * m_bitsPer10Frames / 80 ;
269 }
270
271 return buffered;
272}
273
274bool AudioOutputAudioTrack::AddData(void *in_buffer, int in_len,
275 std::chrono::milliseconds timecode, int in_frames)
276{
277 bool ret = AudioOutputBase::AddData
278 (in_buffer, in_len, timecode,in_frames);
279
280 return ret;
281}
282
284{
286 if (m_audioTrack)
287 {
288 jboolean param = paused;
289 m_audioTrack->callMethod<void>("pause","(Z)V",param);
290 }
291}
292
294{
296 if (m_sourceBitRate > 0
297 && (m_passthru || m_enc)
298 && m_audioTrack)
299 {
301 jint bitsPer10Frames = m_bitsPer10Frames;
302 m_audioTrack->callMethod<void>("setBitsPer10Frames","(I)V",bitsPer10Frames);
303 }
304}
305
307{
309 if (m_audioTrack)
310 {
311 m_audioTrack->callMethod<void>("setOutputThread","(Z)V",true);
313 }
314
316}
317
319{
321 if (m_audioTrack)
322 {
323 m_audioTrack->callMethod<void>("setOutputThread","(Z)V",false);
325 }
326
328}
#define QAndroidJniEnvironment
#define LOC
static constexpr uint8_t CHANNELS_MAX
#define ANDROID_EXCEPTION_CLEAR
static constexpr uint8_t CHANNELS_MIN
#define ANDROID_EXCEPTION_CHECK
#define QAndroidJniObject
@ AF_ENCODING_PCM_8BIT
@ AF_ENCODING_PCM_16BIT
@ AF_CHANNEL_OUT_MONO
@ AF_ENCODING_AC3
@ AF_ENCODING_DOLBY_TRUEHD
@ AF_ENCODING_DTS
@ AF_ENCODING_PCM_FLOAT
@ AF_ENCODING_E_AC3
@ FORMAT_U8
@ FORMAT_FLT
@ FORMAT_S16
void Pause(bool paused) override
AudioOutputSettings * GetOutputSettings(bool digital) override
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
void StopOutputThread(void) override
bool OpenDevice(void) override
QAndroidJniObject * m_audioTrack
bool AddData(void *buffer, int len, std::chrono::milliseconds timecode, int frames) override
Add data to the audiobuffer for playback.
AudioOutputAudioTrack(const AudioSettings &settings)
void CloseDevice(void) override
bool StartOutputThread(void) override
void WriteAudio(unsigned char *aubuf, int size) override
void SetSourceBitrate(int rate) override
void KillAudio(void)
Kill the output thread and cleanup.
virtual void StopOutputThread(void)
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
virtual bool StartOutputThread(void)
AudioFormat m_outputFormat
void InitSettings(const AudioSettings &settings)
bool AddData(void *buffer, int len, std::chrono::milliseconds timecode, int frames) override
Add data to the audiobuffer and perform any required processing.
void Pause(bool paused) override
void SetSourceBitrate(int rate) override
Set the bitrate of the source material, reported in periodic AudioOutput::Events.
const int & channels() const
Definition: audiooutput.h:254
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
Definition: audiosettings.h:85
unsigned int uint
Definition: compat.h:60
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39