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#else
6#include <QJniEnvironment>
7#include <QJniObject>
8#define QAndroidJniEnvironment QJniEnvironment
9#define QAndroidJniObject QJniObject
10#endif
11#include <android/log.h>
12
15
16#define CHANNELS_MIN 1
17#define CHANNELS_MAX 8
18
19#define ANDROID_EXCEPTION_CHECK \
20 if (env->ExceptionCheck()) { \
21 env->ExceptionDescribe(); \
22 env->ExceptionClear(); \
23 exception=true; \
24 } else \
25 exception=false;
26// clear exception without checking
27#define ANDROID_EXCEPTION_CLEAR \
28 if (env->ExceptionCheck()) { \
29 env->ExceptionDescribe(); \
30 env->ExceptionClear(); \
31 }
32
33#define LOC QString("AudioTrack: ")
34
35// Constants from Android Java API
36// class android.media.AudioFormat
37#define AF_CHANNEL_OUT_MONO 4
38#define AF_ENCODING_AC3 5
39#define AF_ENCODING_E_AC3 6
40#define AF_ENCODING_DTS 7
41#define AF_ENCODING_DOLBY_TRUEHD 14
42#define AF_ENCODING_PCM_8BIT 3
43#define AF_ENCODING_PCM_16BIT 2
44#define AF_ENCODING_PCM_FLOAT 4
45
46// for debugging
47#include <android/log.h>
48
50 AudioOutputBase(settings)
51{
52 InitSettings(settings);
53 if (settings.m_init)
54 Reconfigure(settings);
55}
56
58{
59 KillAudio();
60}
61
63{
64 bool exception=false;
66 jint encoding = 0;
67 jint sampleRate = m_sampleRate;
68
69 // m_bitsPer10Frames = output bits per 10 frames
71
72 if ((m_passthru || m_enc) && m_sourceBitRate > 0)
74
75 // 50 milliseconds
77
78 if (m_fragmentSize < 1536)
79 m_fragmentSize = 1536;
80
81
82 if (m_passthru || m_enc)
83 {
84 switch (m_codec)
85 {
86 case AV_CODEC_ID_AC3:
87 encoding = AF_ENCODING_AC3;
88 break;
89 case AV_CODEC_ID_DTS:
90 encoding = AF_ENCODING_DTS;
91 break;
92 case AV_CODEC_ID_EAC3:
93 encoding = AF_ENCODING_E_AC3;
94 break;
95 case AV_CODEC_ID_TRUEHD:
96 encoding = AF_ENCODING_DOLBY_TRUEHD;
97 break;
98
99 default:
100 LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + QString(" No support for audio passthru encoding %1").arg(m_codec));
101 return false;
102 }
103 }
104 else
105 {
106 switch (m_outputFormat)
107 {
108 case FORMAT_U8:
109 // This could be used to get the value from java instead // of haning these constants in pour header file.
110 // encoding = QAndroidJniObject::getStaticField<jint>
111 // ("android.media.AudioFormat","ENCODING_PCM_8BIT");
112 encoding = AF_ENCODING_PCM_8BIT;
113 break;
114 case FORMAT_S16:
115 encoding = AF_ENCODING_PCM_16BIT;
116 break;
117 case FORMAT_FLT:
118 encoding = AF_ENCODING_PCM_FLOAT;
119 break;
120 default:
121 LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + QString(" No support for audio format %1").arg(m_outputFormat));
122 return false;
123 }
124 }
125
126 jint minBufferSize = m_fragmentSize * 4;
127 m_soundcardBufferSize = minBufferSize;
128 jint channels = m_channels;
129
130 m_audioTrack = new QAndroidJniObject("org/mythtv/audio/AudioOutputAudioTrack",
131 "(IIII)V", encoding, sampleRate, minBufferSize, channels);
133
134 if (exception)
135 {
136 LOG(VB_GENERAL, LOG_ERR, LOC + __func__ + QString(" Java Exception when creating AudioTrack"));
137 m_audioTrack = nullptr;
138 return false;
139 }
140 if (!m_passthru && !m_enc)
141 {
142 jint bitsPer10Frames = m_bitsPer10Frames;
143 m_audioTrack->callMethod<void>("setBitsPer10Frames","(I)V",bitsPer10Frames);
144 }
145 return true;
146}
147
149{
151 if (m_audioTrack)
152 {
153 m_audioTrack->callMethod<void>("release");
155 delete m_audioTrack;
156 m_audioTrack = nullptr;
157 }
158}
159
161{
162 bool exception=false;
164 jint bufsize = 0;
165
167
168 int supportedrate = 0;
169 while (int rate = settings->GetNextRate())
170 {
171 // Checking for valid rates using getMinBufferSize.
172 // See https://stackoverflow.com/questions/8043387/android-audiorecord-supported-sampling-rates/22317382
173 bufsize = QAndroidJniObject::callStaticMethod<jint>
174 ("android/media/AudioTrack", "getMinBufferSize", "(III)I",
177 if (bufsize > 0 && !exception)
178 {
179 settings->AddSupportedRate(rate);
180 // save any supported rate for later
181 supportedrate = rate;
182 }
183 }
184
185 // Checking for valid format using getMinBufferSize.
186 bufsize = QAndroidJniObject::callStaticMethod<jint>
187 ("android/media/AudioTrack", "getMinBufferSize", "(III)I",
190 if (bufsize > 0 && !exception)
191 settings->AddSupportedFormat(FORMAT_U8);
192 // 16bit always supported
194
195 bufsize = QAndroidJniObject::callStaticMethod<jint>
196 ("android/media/AudioTrack", "getMinBufferSize", "(III)I",
199 if (bufsize > 0 && !exception)
201
203 {
205 }
206 settings->setPassthrough(0);
207
208 return settings;
209}
210
211void AudioOutputAudioTrack::WriteAudio(unsigned char* aubuf, int size)
212{
213 bool exception=false;
216 {
217 if (m_audioTrack)
218 {
219 jboolean param = true;
220 m_audioTrack->callMethod<void>("pause","(Z)V",param);
222 }
223 return;
224 }
225 // create a java byte array
226 jbyteArray arr = env->NewByteArray(size);
227 env->SetByteArrayRegion(arr, 0, size, reinterpret_cast<jbyte*>(aubuf));
228 jint ret = -99;
229 if (m_audioTrack)
230 {
231 ret = m_audioTrack->callMethod<jint>("write","([BI)I", arr, size);
233 }
234 env->DeleteLocalRef(arr);
235 if (ret != size || exception)
236 LOG(VB_GENERAL, LOG_ERR, LOC + __func__
237 + QString(" Audio Write failed, size %1 return %2 exception %3")
238 .arg(size).arg(ret).arg(exception));
239
240 LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO, LOC + __func__
241 + QString(" WriteAudio size=%1 written=%2")
242 .arg(size).arg(ret));
243}
244
245
247{
249 int buffered (0);
250 if (m_audioTrack)
251 {
252 // This may return a negative value, because there
253 // is data already played that is still in the "Audio circular buffer"
254 buffered
255 = m_audioTrack->callMethod<jint>("getBufferedBytes");
256 bool exception=false;
258 if (exception)
259 buffered = 0;
260 int latency
261 = m_audioTrack->callMethod<jint>("getLatencyViaHeadPosition");
263 if (exception)
264 latency = 0;
265 buffered += latency * m_sampleRate / 1000 * m_bitsPer10Frames / 80 ;
266 }
267
268 return buffered;
269}
270
271bool AudioOutputAudioTrack::AddData(void *in_buffer, int in_len,
272 std::chrono::milliseconds timecode, int in_frames)
273{
274 bool ret = AudioOutputBase::AddData
275 (in_buffer, in_len, timecode,in_frames);
276
277 return ret;
278}
279
281{
283 if (m_audioTrack)
284 {
285 jboolean param = paused;
286 m_audioTrack->callMethod<void>("pause","(Z)V",param);
287 }
288}
289
291{
293 if (m_sourceBitRate > 0
294 && (m_passthru || m_enc)
295 && m_audioTrack)
296 {
298 jint bitsPer10Frames = m_bitsPer10Frames;
299 m_audioTrack->callMethod<void>("setBitsPer10Frames","(I)V",bitsPer10Frames);
300 }
301}
302
304{
306 if (m_audioTrack)
307 {
308 m_audioTrack->callMethod<void>("setOutputThread","(Z)V",true);
310 }
311
313}
314
316{
318 if (m_audioTrack)
319 {
320 m_audioTrack->callMethod<void>("setOutputThread","(Z)V",false);
322 }
323
325}
#define AF_ENCODING_E_AC3
#define QAndroidJniEnvironment
#define LOC
#define AF_ENCODING_PCM_FLOAT
#define CHANNELS_MAX
#define AF_ENCODING_AC3
#define AF_CHANNEL_OUT_MONO
#define CHANNELS_MIN
#define ANDROID_EXCEPTION_CLEAR
#define AF_ENCODING_PCM_8BIT
#define AF_ENCODING_DOLBY_TRUEHD
#define AF_ENCODING_DTS
#define ANDROID_EXCEPTION_CHECK
#define QAndroidJniObject
#define AF_ENCODING_PCM_16BIT
@ 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.
void AddSupportedRate(int rate)
void setPassthrough(int val)
void AddSupportedChannels(int channels)
void AddSupportedFormat(AudioFormat format)
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