1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
|
/*
* Copyright (C) 2010-2018 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
* See LICENSES/README.md for more information.
*/
#include "AESinkAUDIOTRACK.h"
#include "ServiceBroker.h"
#include "cores/AudioEngine/AESinkFactory.h"
#include "cores/AudioEngine/Utils/AEUtil.h"
#include "settings/Settings.h"
#include "utils/StringUtils.h"
#include "utils/TimeUtils.h"
#include "utils/log.h"
#include "platform/android/activity/XBMCApp.h"
#include <androidjni/AudioFormat.h>
#include <androidjni/AudioManager.h>
#include <androidjni/AudioTrack.h>
#include <androidjni/Build.h>
#include <unistd.h>
// This is an alternative to the linear weighted delay smoothing
// advantages: only one history value needs to be stored
// in tests the linear weighted average smoother yield better results
//#define AT_USE_EXPONENTIAL_AVERAGING 1
using namespace jni;
using namespace std::chrono_literals;
// those are empirical values while the HD buffer
// is the max TrueHD package
const unsigned int MAX_RAW_AUDIO_BUFFER_HD = 61440;
const unsigned int MAX_RAW_AUDIO_BUFFER = 16384;
const unsigned int MOVING_AVERAGE_MAX_MEMBERS = 3;
const uint64_t UINT64_LOWER_BYTES = 0x00000000FFFFFFFF;
const uint64_t UINT64_UPPER_BYTES = 0xFFFFFFFF00000000;
static const AEChannel KnownChannels[] = {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_LFE,
AE_CH_SL, AE_CH_SR, AE_CH_BL, AE_CH_BR,
AE_CH_BC, AE_CH_BLOC, AE_CH_BROC, AE_CH_NULL};
static int AEStreamFormatToATFormat(const CAEStreamInfo::DataType& dt)
{
switch (dt)
{
case CAEStreamInfo::STREAM_TYPE_AC3:
return CJNIAudioFormat::ENCODING_AC3;
case CAEStreamInfo::STREAM_TYPE_DTS_512:
case CAEStreamInfo::STREAM_TYPE_DTS_1024:
case CAEStreamInfo::STREAM_TYPE_DTS_2048:
case CAEStreamInfo::STREAM_TYPE_DTSHD_CORE:
return CJNIAudioFormat::ENCODING_DTS;
case CAEStreamInfo::STREAM_TYPE_DTSHD:
case CAEStreamInfo::STREAM_TYPE_DTSHD_MA:
return CJNIAudioFormat::ENCODING_DTS_HD;
case CAEStreamInfo::STREAM_TYPE_EAC3:
return CJNIAudioFormat::ENCODING_E_AC3;
case CAEStreamInfo::STREAM_TYPE_TRUEHD:
return CJNIAudioFormat::ENCODING_DOLBY_TRUEHD;
default:
return CJNIAudioFormat::ENCODING_PCM_16BIT;
}
}
static AEChannel AUDIOTRACKChannelToAEChannel(int atChannel)
{
AEChannel aeChannel;
/* cannot use switch since CJNIAudioFormat is populated at runtime */
if (atChannel == CJNIAudioFormat::CHANNEL_OUT_FRONT_LEFT) aeChannel = AE_CH_FL;
else if (atChannel == CJNIAudioFormat::CHANNEL_OUT_FRONT_RIGHT) aeChannel = AE_CH_FR;
else if (atChannel == CJNIAudioFormat::CHANNEL_OUT_FRONT_CENTER) aeChannel = AE_CH_FC;
else if (atChannel == CJNIAudioFormat::CHANNEL_OUT_LOW_FREQUENCY) aeChannel = AE_CH_LFE;
else if (atChannel == CJNIAudioFormat::CHANNEL_OUT_BACK_LEFT) aeChannel = AE_CH_BL;
else if (atChannel == CJNIAudioFormat::CHANNEL_OUT_BACK_RIGHT) aeChannel = AE_CH_BR;
else if (atChannel == CJNIAudioFormat::CHANNEL_OUT_SIDE_LEFT) aeChannel = AE_CH_SL;
else if (atChannel == CJNIAudioFormat::CHANNEL_OUT_SIDE_RIGHT) aeChannel = AE_CH_SR;
else if (atChannel == CJNIAudioFormat::CHANNEL_OUT_FRONT_LEFT_OF_CENTER) aeChannel = AE_CH_FLOC;
else if (atChannel == CJNIAudioFormat::CHANNEL_OUT_FRONT_RIGHT_OF_CENTER) aeChannel = AE_CH_FROC;
else if (atChannel == CJNIAudioFormat::CHANNEL_OUT_BACK_CENTER) aeChannel = AE_CH_BC;
else aeChannel = AE_CH_UNKNOWN1;
return aeChannel;
}
static int AEChannelToAUDIOTRACKChannel(AEChannel aeChannel)
{
int atChannel;
switch (aeChannel)
{
case AE_CH_FL: atChannel = CJNIAudioFormat::CHANNEL_OUT_FRONT_LEFT; break;
case AE_CH_FR: atChannel = CJNIAudioFormat::CHANNEL_OUT_FRONT_RIGHT; break;
case AE_CH_FC: atChannel = CJNIAudioFormat::CHANNEL_OUT_FRONT_CENTER; break;
case AE_CH_LFE: atChannel = CJNIAudioFormat::CHANNEL_OUT_LOW_FREQUENCY; break;
case AE_CH_BL: atChannel = CJNIAudioFormat::CHANNEL_OUT_BACK_LEFT; break;
case AE_CH_BR: atChannel = CJNIAudioFormat::CHANNEL_OUT_BACK_RIGHT; break;
case AE_CH_SL: atChannel = CJNIAudioFormat::CHANNEL_OUT_SIDE_LEFT; break;
case AE_CH_SR: atChannel = CJNIAudioFormat::CHANNEL_OUT_SIDE_RIGHT; break;
case AE_CH_BC: atChannel = CJNIAudioFormat::CHANNEL_OUT_BACK_CENTER; break;
case AE_CH_FLOC: atChannel = CJNIAudioFormat::CHANNEL_OUT_FRONT_LEFT_OF_CENTER; break;
case AE_CH_FROC: atChannel = CJNIAudioFormat::CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break;
default: atChannel = CJNIAudioFormat::CHANNEL_INVALID; break;
}
return atChannel;
}
static CAEChannelInfo AUDIOTRACKChannelMaskToAEChannelMap(int atMask)
{
CAEChannelInfo info;
int mask = 0x1;
for (unsigned int i = 0; i < sizeof(int32_t) * 8; i++)
{
if (atMask & mask)
info += AUDIOTRACKChannelToAEChannel(mask);
mask <<= 1;
}
return info;
}
static int AEChannelMapToAUDIOTRACKChannelMask(CAEChannelInfo info)
{
info.ResolveChannels(CAEChannelInfo(KnownChannels));
// Detect layouts with 6 channels including one LFE channel
// We currently support the following layouts:
// 5.1 FL+FR+FC+LFE+BL+BR
// 5.1(side) FL+FR+FC+LFE+SL+SR
// According to CEA-861-D only RR and RL are defined
// Therefore we let Android decide about the 5.1 mapping
// For 8 channel layouts including one LFE channel
// we leave the same decision to Android
if (info.Count() == 6 && info.HasChannel(AE_CH_LFE))
return CJNIAudioFormat::CHANNEL_OUT_5POINT1;
if (info.Count() == 8 && info.HasChannel(AE_CH_LFE))
return CJNIAudioFormat::CHANNEL_OUT_7POINT1_SURROUND;
int atMask = 0;
for (unsigned int i = 0; i < info.Count(); i++)
atMask |= AEChannelToAUDIOTRACKChannel(info[i]);
return atMask;
}
jni::CJNIAudioTrack *CAESinkAUDIOTRACK::CreateAudioTrack(int stream, int sampleRate, int channelMask, int encoding, int bufferSize)
{
jni::CJNIAudioTrack *jniAt = NULL;
try
{
CJNIAudioAttributesBuilder attrBuilder;
attrBuilder.setUsage(CJNIAudioAttributes::USAGE_MEDIA);
attrBuilder.setContentType(CJNIAudioAttributes::CONTENT_TYPE_MUSIC);
CJNIAudioFormatBuilder fmtBuilder;
fmtBuilder.setChannelMask(channelMask);
fmtBuilder.setEncoding(encoding);
fmtBuilder.setSampleRate(sampleRate);
jniAt = new CJNIAudioTrack(attrBuilder.build(),
fmtBuilder.build(),
bufferSize,
CJNIAudioTrack::MODE_STREAM,
CJNIAudioManager::AUDIO_SESSION_ID_GENERATE);
}
catch (const std::invalid_argument& e)
{
CLog::Log(LOGINFO, "AESinkAUDIOTRACK - AudioTrack creation (channelMask {:#08x}): {}",
channelMask, e.what());
}
return jniAt;
}
int CAESinkAUDIOTRACK::AudioTrackWrite(char* audioData, int offsetInBytes, int sizeInBytes)
{
int written = 0;
if (m_jniAudioFormat == CJNIAudioFormat::ENCODING_PCM_FLOAT)
{
if (m_floatbuf.size() != (sizeInBytes - offsetInBytes) / sizeof(float))
m_floatbuf.resize((sizeInBytes - offsetInBytes) / sizeof(float));
memcpy(m_floatbuf.data(), audioData + offsetInBytes, sizeInBytes - offsetInBytes);
written = m_at_jni->write(m_floatbuf, 0, (sizeInBytes - offsetInBytes) / sizeof(float), CJNIAudioTrack::WRITE_BLOCKING);
written *= sizeof(float);
}
else if (m_jniAudioFormat == CJNIAudioFormat::ENCODING_IEC61937)
{
if (m_shortbuf.size() != (sizeInBytes - offsetInBytes) / sizeof(int16_t))
m_shortbuf.resize((sizeInBytes - offsetInBytes) / sizeof(int16_t));
memcpy(m_shortbuf.data(), audioData + offsetInBytes, sizeInBytes - offsetInBytes);
if (CJNIBase::GetSDKVersion() >= 23)
written = m_at_jni->write(m_shortbuf, 0, (sizeInBytes - offsetInBytes) / sizeof(int16_t), CJNIAudioTrack::WRITE_BLOCKING);
else
written = m_at_jni->write(m_shortbuf, 0, (sizeInBytes - offsetInBytes) / sizeof(int16_t));
written *= sizeof(uint16_t);
}
else
{
if (static_cast<int>(m_charbuf.size()) != (sizeInBytes - offsetInBytes))
m_charbuf.resize(sizeInBytes - offsetInBytes);
memcpy(m_charbuf.data(), audioData + offsetInBytes, sizeInBytes - offsetInBytes);
if (CJNIBase::GetSDKVersion() >= 23)
written = m_at_jni->write(m_charbuf, 0, sizeInBytes - offsetInBytes, CJNIAudioTrack::WRITE_BLOCKING);
else
written = m_at_jni->write(m_charbuf, 0, sizeInBytes - offsetInBytes);
}
return written;
}
int CAESinkAUDIOTRACK::AudioTrackWrite(char* audioData, int sizeInBytes, int64_t timestamp)
{
int written = 0;
std::vector<char> buf;
buf.reserve(sizeInBytes);
memcpy(buf.data(), audioData, sizeInBytes);
CJNIByteBuffer bytebuf = CJNIByteBuffer::wrap(buf);
written = m_at_jni->write(bytebuf.get_raw(), sizeInBytes, CJNIAudioTrack::WRITE_BLOCKING, timestamp);
return written;
}
CAEDeviceInfo CAESinkAUDIOTRACK::m_info;
CAEDeviceInfo CAESinkAUDIOTRACK::m_info_iec;
CAEDeviceInfo CAESinkAUDIOTRACK::m_info_raw;
bool CAESinkAUDIOTRACK::m_hasIEC = false;
std::set<unsigned int> CAESinkAUDIOTRACK::m_sink_sampleRates;
bool CAESinkAUDIOTRACK::m_sinkSupportsFloat = false;
bool CAESinkAUDIOTRACK::m_sinkSupportsMultiChannelFloat = false;
bool CAESinkAUDIOTRACK::m_passthrough_use_eac3 = false;
////////////////////////////////////////////////////////////////////////////////////////////
CAESinkAUDIOTRACK::CAESinkAUDIOTRACK()
{
m_alignedS16 = NULL;
m_sink_frameSize = 0;
m_encoding = CJNIAudioFormat::ENCODING_PCM_16BIT;
m_audiotrackbuffer_sec = 0.0;
m_at_jni = NULL;
m_duration_written = 0;
m_headPos = 0;
m_timestampPos = 0;
m_sink_sampleRate = 0;
m_passthrough = false;
m_min_buffer_size = 0;
}
CAESinkAUDIOTRACK::~CAESinkAUDIOTRACK()
{
Deinitialize();
}
bool CAESinkAUDIOTRACK::VerifySinkConfiguration(int sampleRate,
int channelMask,
int encoding,
bool isRaw)
{
int minBufferSize = CJNIAudioTrack::getMinBufferSize(sampleRate, channelMask, encoding);
bool supported = (minBufferSize > 0);
// make sure to have enough buffer as minimum might not be enough to open
if (!isRaw)
minBufferSize *= 2;
if (supported)
{
jni::CJNIAudioTrack* jniAt = CreateAudioTrack(CJNIAudioManager::STREAM_MUSIC, sampleRate,
channelMask, encoding, minBufferSize);
supported = (jniAt && jniAt->getState() == CJNIAudioTrack::STATE_INITIALIZED);
if (supported)
{
jniAt->pause();
jniAt->flush();
}
if (jniAt)
{
jniAt->release();
delete jniAt;
}
}
CLog::Log(LOGDEBUG, "VerifySinkConfiguration samplerate: {} mask: {} encoding: {} success: {}",
sampleRate, channelMask, encoding, supported ? "true" : "false");
return supported;
}
bool CAESinkAUDIOTRACK::IsSupported(int sampleRateInHz, int channelConfig, int encoding)
{
int ret = CJNIAudioTrack::getMinBufferSize( sampleRateInHz, channelConfig, encoding);
return (ret > 0);
}
bool CAESinkAUDIOTRACK::Initialize(AEAudioFormat &format, std::string &device)
{
// try to recover used device
if (!m_hasIEC)
m_info = m_info_raw;
else if (device == "Default" && !m_info.m_wantsIECPassthrough)
m_info = m_info_raw;
else if (device == "AudioTrack (RAW)")
m_info = m_info_raw;
else
m_info = m_info_iec;
m_format = format;
m_headPos = 0;
m_timestampPos = 0;
m_linearmovingaverage.clear();
m_pause_ms = 0.0;
CLog::Log(LOGDEBUG,
"CAESinkAUDIOTRACK::Initialize requested: sampleRate {}; format: {}; channels: {}",
format.m_sampleRate, CAEUtil::DataFormatToStr(format.m_dataFormat),
format.m_channelLayout.Count());
int stream = CJNIAudioManager::STREAM_MUSIC;
m_encoding = CJNIAudioFormat::ENCODING_PCM_16BIT;
// If the device supports EAC3 passthrough, but not basic AC3 patthrough, send it as EAC3 (which is AC3 compatible) instead
if (!m_info.m_wantsIECPassthrough)
{
if ((m_format.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_AC3) && m_passthrough_use_eac3)
m_format.m_streamInfo.m_type = CAEStreamInfo::STREAM_TYPE_EAC3;
}
uint32_t distance = UINT32_MAX; // max upper distance, update at least ones to use one of our samplerates
for (auto& s : m_sink_sampleRates)
{
// prefer best match or alternatively something that divides nicely and
// is not too far away
uint32_t d = std::abs((int)m_format.m_sampleRate - (int)s) + 8 * (s > m_format.m_sampleRate ? (s % m_format.m_sampleRate) : (m_format.m_sampleRate % s));
if (d < distance)
{
m_sink_sampleRate = s;
distance = d;
CLog::Log(LOGDEBUG, "Updated SampleRate: {} Distance: {}", m_sink_sampleRate, d);
}
}
if (m_format.m_dataFormat == AE_FMT_RAW)
{
m_passthrough = true;
m_encoding = AEStreamFormatToATFormat(m_format.m_streamInfo.m_type);
m_format.m_channelLayout = AE_CH_LAYOUT_2_0;
if (m_format.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_DTSHD_MA ||
m_format.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_TRUEHD)
{
m_format.m_channelLayout = AE_CH_LAYOUT_7_1;
}
// EAC3 needs real samplerate not the modulation
if (m_format.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_EAC3)
m_sink_sampleRate = m_format.m_streamInfo.m_sampleRate;
if (m_info.m_wantsIECPassthrough)
{
m_format.m_dataFormat = AE_FMT_S16LE;
if (m_format.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_DTSHD ||
m_format.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_DTSHD_MA ||
m_format.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_TRUEHD)
m_sink_sampleRate = 192000;
// new Android N format
if (CJNIAudioFormat::ENCODING_IEC61937 != -1)
{
m_encoding = CJNIAudioFormat::ENCODING_IEC61937;
// this will be sent tunneled, therefore the IEC path needs e.g.
// 4 * m_format.m_streamInfo.m_sampleRate
if (m_format.m_streamInfo.m_type == CAEStreamInfo::STREAM_TYPE_EAC3)
m_sink_sampleRate = m_format.m_sampleRate;
}
// we are running on an old android version
// that does neither know AC3, DTS or whatever
// we will fallback to 16BIT passthrough
if (m_encoding == -1)
{
m_format.m_channelLayout = AE_CH_LAYOUT_2_0;
m_format.m_sampleRate = m_sink_sampleRate;
m_encoding = CJNIAudioFormat::ENCODING_PCM_16BIT;
CLog::Log(LOGDEBUG, "Fallback to PCM passthrough mode - this might not work!");
}
}
}
else
{
m_passthrough = false;
m_format.m_sampleRate = m_sink_sampleRate;
if (m_sinkSupportsMultiChannelFloat)
{
m_encoding = CJNIAudioFormat::ENCODING_PCM_FLOAT;
m_format.m_dataFormat = AE_FMT_FLOAT;
}
else if (m_sinkSupportsFloat && m_format.m_channelLayout.Count() == 2)
{
m_encoding = CJNIAudioFormat::ENCODING_PCM_FLOAT;
m_format.m_dataFormat = AE_FMT_FLOAT;
}
else
{
m_encoding = CJNIAudioFormat::ENCODING_PCM_16BIT;
m_format.m_dataFormat = AE_FMT_S16LE;
}
}
int atChannelMask = AEChannelMapToAUDIOTRACKChannelMask(m_format.m_channelLayout);
m_format.m_channelLayout = AUDIOTRACKChannelMaskToAEChannelMap(atChannelMask);
if (m_encoding == CJNIAudioFormat::ENCODING_IEC61937)
{
// keep above channel output if we do IEC61937 and got DTSHD or TrueHD by AudioEngine
if (m_format.m_streamInfo.m_type != CAEStreamInfo::STREAM_TYPE_DTSHD_MA && m_format.m_streamInfo.m_type != CAEStreamInfo::STREAM_TYPE_TRUEHD)
atChannelMask = CJNIAudioFormat::CHANNEL_OUT_STEREO;
}
bool retried = false;
while (!m_at_jni)
{
CLog::Log(LOGINFO, "Trying to open: samplerate: {}, channelMask: {}, encoding: {}",
m_sink_sampleRate, atChannelMask, m_encoding);
int min_buffer = CJNIAudioTrack::getMinBufferSize(m_sink_sampleRate,
atChannelMask,
m_encoding);
if (min_buffer < 0)
{
CLog::Log(LOGERROR,
"Minimum Buffer Size was: {} - disable passthrough (?) your hw does not support it",
min_buffer);
return false;
}
m_min_buffer_size = (unsigned int) min_buffer;
CLog::Log(LOGINFO, "Minimum size we need for stream: {} Bytes", m_min_buffer_size);
double rawlength_in_seconds = 0.0;
int multiplier = 1;
unsigned int ac3FrameSize = 1;
if (m_passthrough && !m_info.m_wantsIECPassthrough)
{
switch (m_format.m_streamInfo.m_type)
{
case CAEStreamInfo::STREAM_TYPE_TRUEHD:
m_min_buffer_size = MAX_RAW_AUDIO_BUFFER_HD;
m_format.m_frames = m_min_buffer_size;
rawlength_in_seconds = 8 * m_format.m_streamInfo.GetDuration() / 1000; // on average
break;
case CAEStreamInfo::STREAM_TYPE_DTSHD_MA:
case CAEStreamInfo::STREAM_TYPE_DTSHD:
// normal frame is max 2012 bytes + 2764 sub frame
m_min_buffer_size = 66432; //according to the buffer model of ISO/IEC13818-1
m_format.m_frames = m_min_buffer_size;
rawlength_in_seconds = 8 * m_format.m_streamInfo.GetDuration() / 1000; // average value
break;
case CAEStreamInfo::STREAM_TYPE_DTS_512:
case CAEStreamInfo::STREAM_TYPE_DTSHD_CORE:
// max 2012 bytes
// depending on sample rate between 156 ms and 312 ms
m_min_buffer_size = 16 * 2012;
m_format.m_frames = m_min_buffer_size;
rawlength_in_seconds = 16 * m_format.m_streamInfo.GetDuration() / 1000;
break;
case CAEStreamInfo::STREAM_TYPE_DTS_1024:
case CAEStreamInfo::STREAM_TYPE_DTS_2048:
m_min_buffer_size = 8 * 5462;
m_format.m_frames = m_min_buffer_size;
rawlength_in_seconds = 8 * m_format.m_streamInfo.GetDuration() / 1000;
break;
case CAEStreamInfo::STREAM_TYPE_AC3:
ac3FrameSize = m_format.m_streamInfo.m_ac3FrameSize;
if (ac3FrameSize == 0)
ac3FrameSize = 1536; // fallback if not set, e.g. Transcoding
m_min_buffer_size = std::max(m_min_buffer_size * 3, ac3FrameSize * 8);
m_format.m_frames = m_min_buffer_size;
multiplier = m_min_buffer_size / ac3FrameSize; // int division is wanted
rawlength_in_seconds = multiplier * m_format.m_streamInfo.GetDuration() / 1000;
break;
// EAC3 is currently not supported
case CAEStreamInfo::STREAM_TYPE_EAC3:
m_min_buffer_size = 2 * 10752; // least common multiple of 1792 and 1536
m_format.m_frames = m_min_buffer_size; // needs testing
rawlength_in_seconds = 8 * m_format.m_streamInfo.GetDuration() / 1000;
break;
default:
m_min_buffer_size = MAX_RAW_AUDIO_BUFFER;
m_format.m_frames = m_min_buffer_size;
rawlength_in_seconds = 0.4;
break;
}
CLog::Log(LOGDEBUG, "Opening Passthrough RAW Format: {} Sink SampleRate: {}",
CAEUtil::StreamTypeToStr(m_format.m_streamInfo.m_type), m_sink_sampleRate);
m_format.m_frameSize = 1;
m_sink_frameSize = m_format.m_frameSize;
}
else
{
m_format.m_frameSize = m_format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(m_format.m_dataFormat) / 8);
m_sink_frameSize = m_format.m_frameSize;
// aim at max 200 ms buffer and 50 ms periods but at least two periods of min_buffer
// make sure periods are actually not smaller than 32 ms (32, cause 32 * 2 = 64)
// but also lower than 64 ms
// which is large enough to not cause CPU hogging in case 32 ms periods are used
m_audiotrackbuffer_sec =
static_cast<double>(m_min_buffer_size) / (m_sink_frameSize * m_sink_sampleRate);
// the period calculation starts
// after the buffer division to get even division results
int c = 1;
if (m_audiotrackbuffer_sec > 0.25)
{
CLog::Log(LOGWARNING,
"Audiobuffer is already very large {:f} ms - Reducing to a sensible value",
1000.0 * m_audiotrackbuffer_sec);
int buffer_frames = m_sink_sampleRate / 4; // 250 ms
m_min_buffer_size = buffer_frames * m_sink_frameSize;
c = 5; // 50 ms
}
// update potential new buffertime
m_audiotrackbuffer_sec =
static_cast<double>(m_min_buffer_size) / (m_sink_frameSize * m_sink_sampleRate);
constexpr double max_time = 0.064;
constexpr double min_time = 0.032;
constexpr double target_duration = 0.128;
while (m_audiotrackbuffer_sec < target_duration)
{
m_min_buffer_size += min_buffer;
c++;
m_audiotrackbuffer_sec =
static_cast<double>(m_min_buffer_size) / (m_sink_frameSize * m_sink_sampleRate);
}
unsigned int period_size = m_min_buffer_size / c;
double period_time =
static_cast<double>(period_size) / (m_sink_frameSize * m_sink_sampleRate);
// This will result in minimum 32 ms
while (period_time >= max_time)
{
period_time /= 2;
period_size /= 2;
}
// If the audio track API gave us very low values increase them
// In this case the first loop would not have been run at all
while (period_time < min_time)
{
period_size *= 2;
period_time *= 2;
}
m_format.m_frames = static_cast<int>(period_size / m_format.m_frameSize);
CLog::Log(LOGINFO,
"Audiotrack buffer params are: period time = {:.3f} ms, period size = "
"{} bytes, num periods = {}",
period_time * 1000, period_size, m_min_buffer_size / period_size);
}
if (m_passthrough && !m_info.m_wantsIECPassthrough)
m_audiotrackbuffer_sec = rawlength_in_seconds;
CLog::Log(LOGINFO,
"Created Audiotrackbuffer with playing time of {:f} ms min buffer size: {} bytes",
m_audiotrackbuffer_sec * 1000, m_min_buffer_size);
m_jniAudioFormat = m_encoding;
m_at_jni = CreateAudioTrack(stream, m_sink_sampleRate, atChannelMask,
m_encoding, m_min_buffer_size);
if (!IsInitialized())
{
if (!m_passthrough)
{
if (atChannelMask != CJNIAudioFormat::CHANNEL_OUT_STEREO &&
atChannelMask != CJNIAudioFormat::CHANNEL_OUT_5POINT1)
{
atChannelMask = CJNIAudioFormat::CHANNEL_OUT_5POINT1;
CLog::Log(LOGDEBUG, "AESinkAUDIOTRACK - Retrying multichannel playback with a 5.1 layout");
continue;
}
else if (atChannelMask != CJNIAudioFormat::CHANNEL_OUT_STEREO)
{
atChannelMask = CJNIAudioFormat::CHANNEL_OUT_STEREO;
CLog::Log(LOGDEBUG, "AESinkAUDIOTRACK - Retrying with a stereo layout");
continue;
}
}
else
{
if (!retried)
{
retried = true;
CLog::Log(LOGWARNING, "AESinkAUDIOTRACK - Unable to open PT device - will retry once");
// Seems that some devices don't properly implement pause + flush, which during seek
// might open the device too fast - let's retry
usleep(200 * 1000);
continue;
}
}
CLog::Log(LOGERROR, "AESinkAUDIOTRACK - Unable to create AudioTrack");
Deinitialize();
return false;
}
const char* method = m_passthrough ? (m_info.m_wantsIECPassthrough ? "IEC (PT)" : "RAW (PT)") : "PCM";
CLog::Log(LOGINFO,
"CAESinkAUDIOTRACK::Initializing with: m_sampleRate: {} format: {} (AE) method: {} "
"stream-type: {} min_buffer_size: {} m_frames: {} m_frameSize: {} channels: {}",
m_sink_sampleRate, CAEUtil::DataFormatToStr(m_format.m_dataFormat), method,
m_passthrough ? CAEUtil::StreamTypeToStr(m_format.m_streamInfo.m_type) : "PCM-STREAM",
m_min_buffer_size, m_format.m_frames, m_format.m_frameSize,
m_format.m_channelLayout.Count());
}
format = m_format;
return true;
}
void CAESinkAUDIOTRACK::Deinitialize()
{
CLog::Log(LOGDEBUG, "CAESinkAUDIOTRACK::Deinitialize");
if (!m_at_jni)
return;
if (IsInitialized())
{
m_at_jni->pause();
m_at_jni->flush();
}
m_at_jni->release();
m_duration_written = 0;
m_headPos = 0;
m_timestampPos = 0;
m_stampTimer.SetExpired();
m_linearmovingaverage.clear();
delete m_at_jni;
m_at_jni = NULL;
m_delay = 0.0;
m_hw_delay = 0.0;
}
bool CAESinkAUDIOTRACK::IsInitialized()
{
return (m_at_jni && m_at_jni->getState() == CJNIAudioTrack::STATE_INITIALIZED);
}
void CAESinkAUDIOTRACK::GetDelay(AEDelayStatus& status)
{
if (!m_at_jni)
{
status.SetDelay(0);
return;
}
bool usesAdvancedLogging = CServiceBroker::GetLogging().CanLogComponent(LOGAUDIO);
// In their infinite wisdom, Google decided to make getPlaybackHeadPosition
// return a 32bit "int" that you should "interpret as unsigned." As such,
// for wrap safety, we need to do all ops on it in 32bit integer math.
uint32_t head_pos = (uint32_t)m_at_jni->getPlaybackHeadPosition();
// Wraparound
if ((uint32_t)(m_headPos & UINT64_LOWER_BYTES) > head_pos) // need to compute wraparound
m_headPos += (1ULL << 32); // add wraparound, e.g. 0x0000 FFFF FFFF -> 0x0001 FFFF FFFF
// clear lower 32 bit values, e.g. 0x0001 FFFF FFFF -> 0x0001 0000 0000
// and add head_pos which wrapped around, e.g. 0x0001 0000 0000 -> 0x0001 0000 0004
m_headPos = (m_headPos & UINT64_UPPER_BYTES) | (uint64_t)head_pos;
double gone = static_cast<double>(m_headPos) / m_sink_sampleRate;
// if sink is run dry without buffer time written anymore
if (gone > m_duration_written)
gone = m_duration_written;
double delay = m_duration_written - gone;
if (m_stampTimer.IsTimePast())
{
if (!m_at_jni->getTimestamp(m_timestamp))
{
CLog::Log(LOGDEBUG, "Could not acquire timestamp");
m_stampTimer.Set(100ms);
}
else
{
// check if frameposition is valid and nano timer less than 50 ms outdated
if (m_timestamp.get_framePosition() > 0 &&
(CurrentHostCounter() - m_timestamp.get_nanoTime()) < 50 * 1000 * 1000)
m_stampTimer.Set(1000ms);
else
m_stampTimer.Set(100ms);
}
}
// check if last value was received less than 2 seconds ago
if (m_timestamp.get_framePosition() > 0 &&
(CurrentHostCounter() - m_timestamp.get_nanoTime()) < 2 * 1000 * 1000 * 1000)
{
if (usesAdvancedLogging)
{
CLog::Log(LOGINFO, "Framecounter: {} Time: {} Current-Time: {}",
(m_timestamp.get_framePosition() & UINT64_LOWER_BYTES), m_timestamp.get_nanoTime(),
CurrentHostCounter());
}
uint64_t delta = static_cast<uint64_t>(CurrentHostCounter() - m_timestamp.get_nanoTime());
uint64_t stamphead =
static_cast<uint64_t>(m_timestamp.get_framePosition() & UINT64_LOWER_BYTES) +
delta * m_sink_sampleRate / 1000000000.0;
// wrap around
// e.g. 0xFFFFFFFFFFFF0123 -> 0x0000000000002478
// because we only query each second the simple smaller comparison won't suffice
// as delay can fluctuate minimally
if (stamphead < m_timestampPos && (m_timestampPos - stamphead) > 0x7FFFFFFFFFFFFFFFULL)
{
uint64_t stamp = m_timestampPos;
stamp += (1ULL << 32);
stamphead = (stamp & UINT64_UPPER_BYTES) | stamphead;
CLog::Log(LOGDEBUG, "Wraparound happened old: {} new: {}", m_timestampPos, stamphead);
}
m_timestampPos = stamphead;
double playtime = m_timestampPos / static_cast<double>(m_sink_sampleRate);
if (usesAdvancedLogging)
{
CLog::Log(LOGINFO,
"Delay - Timestamp: {} (ms) delta: {} (ms) playtime: {} (ms) Duration: {} ms",
1000.0 * (m_duration_written - playtime), delta / 1000000.0, playtime * 1000,
m_duration_written * 1000);
CLog::Log(LOGINFO, "Head-Position {} Timestamp Position {} Delay-Offset: {} ms", m_headPos,
m_timestampPos,
1000.0 * (static_cast<int64_t>(m_headPos - m_timestampPos)) / m_sink_sampleRate);
}
double hw_delay = m_duration_written - playtime;
// correct by subtracting above measured delay, if lower delay gets automatically reduced
hw_delay -= delay;
// sometimes at the beginning of the stream m_timestampPos is more accurate and ahead of
// m_headPos - don't use the computed value then and wait
if (hw_delay > -1.0 && hw_delay < 1.0)
m_hw_delay = hw_delay;
else
m_hw_delay = 0.0;
if (usesAdvancedLogging)
{
CLog::Log(LOGINFO, "HW-Delay (1): {} ms", hw_delay * 1000);
}
}
delay += m_hw_delay;
if (usesAdvancedLogging)
{
CLog::Log(LOGINFO, "Combined Delay: {} ms", delay * 1000);
}
if (delay < 0.0)
delay = 0.0;
// the RAW hack for simulating pause bursts should not come
// into the way of hw delay
if (m_pause_ms > 0.0)
{
double difference = (m_audiotrackbuffer_sec - delay) * 1000;
if (usesAdvancedLogging)
{
CLog::Log(LOGINFO, "Faking Pause-Bursts in Delay - returning smoothed {} ms Original {} ms",
m_audiotrackbuffer_sec * 1000, delay * 1000);
CLog::Log(LOGINFO, "Difference: {} ms m_pause_ms {}", difference, m_pause_ms);
}
// buffer not yet reached
if (difference > 0.0)
delay = m_audiotrackbuffer_sec;
else
{
CLog::Log(LOGINFO, "Resetting pause bursts as buffer level was reached! (2)");
m_pause_ms = 0.0;
}
}
const double d = GetMovingAverageDelay(delay);
// Audiotrack is caching more than we thought it would
if (d > m_audiotrackbuffer_sec)
m_audiotrackbuffer_sec = d;
// track delay in local member
m_delay = d;
if (usesAdvancedLogging)
{
CLog::Log(LOGINFO, "Delay Current: {:f} ms", d * 1000);
}
status.SetDelay(d);
}
double CAESinkAUDIOTRACK::GetLatency()
{
return 0.0;
}
double CAESinkAUDIOTRACK::GetCacheTotal()
{
// total amount that the audio sink can buffer in units of seconds
return m_audiotrackbuffer_sec;
}
// this method is supposed to block until all frames are written to the device buffer
// when it returns ActiveAESink will take the next buffer out of a queue
unsigned int CAESinkAUDIOTRACK::AddPackets(uint8_t **data, unsigned int frames, unsigned int offset)
{
if (!IsInitialized())
return INT_MAX;
// for debugging only - can be removed if everything is really stable
uint64_t startTime = CurrentHostCounter();
uint8_t *buffer = data[0]+offset*m_format.m_frameSize;
uint8_t *out_buf = buffer;
int size = frames * m_format.m_frameSize;
// write as many frames of audio as we can fit into our internal buffer.
int written = 0;
int loop_written = 0;
if (frames)
{
if (m_at_jni->getPlayState() != CJNIAudioTrack::PLAYSTATE_PLAYING)
m_at_jni->play();
bool retried = false;
int size_left = size;
while (written < size)
{
loop_written = AudioTrackWrite((char*)out_buf, 0, size_left);
written += loop_written;
size_left -= loop_written;
if (loop_written < 0)
{
CLog::Log(LOGERROR, "CAESinkAUDIOTRACK::AddPackets write returned error: {}",
loop_written);
return INT_MAX;
}
// if we could not add any data - sleep a bit and retry
if (loop_written == 0)
{
if (!retried)
{
retried = true;
double sleep_time = 0;
if (m_passthrough && !m_info.m_wantsIECPassthrough)
{
sleep_time = m_format.m_streamInfo.GetDuration();
usleep(sleep_time * 1000);
}
else
{
sleep_time = 1000.0 * m_format.m_frames / m_format.m_sampleRate;
usleep(sleep_time * 1000);
}
bool playing = m_at_jni->getPlayState() == CJNIAudioTrack::PLAYSTATE_PLAYING;
CLog::Log(LOGDEBUG, "Retried to write onto the sink - slept: {:f} playing: {}",
sleep_time, playing ? "yes" : "no");
continue;
}
else
{
CLog::Log(LOGDEBUG, "Repeatedly tried to write onto the sink - giving up");
break;
}
}
retried = false; // at least one time there was more than zero data written
if (m_passthrough && !m_info.m_wantsIECPassthrough)
{
if (written == size)
m_duration_written += m_format.m_streamInfo.GetDuration() / 1000;
else
{
CLog::Log(LOGDEBUG, "Error writing full package to sink, left: {}", size_left);
// Let AE wait some ms to come back
unsigned int written_frames = (unsigned int) (written/m_format.m_frameSize);
return written_frames;
}
}
else
{
double duration =
(static_cast<double>(loop_written) / m_format.m_frameSize) / m_format.m_sampleRate;
m_duration_written += duration;
}
// just try again to care for fragmentation
if (written < size)
out_buf = out_buf + loop_written;
loop_written = 0;
}
}
unsigned int written_frames = static_cast<unsigned int>(written / m_format.m_frameSize);
double time_to_add_ms = 1000.0 * (CurrentHostCounter() - startTime) / CurrentHostFrequency();
if (m_passthrough && !m_info.m_wantsIECPassthrough)
{
// AT does not consume in a blocking way - it runs ahead and blocks
// exactly once with the last package for some 100 ms
double extra_sleep = 0.0;
if (time_to_add_ms < m_format.m_streamInfo.GetDuration())
extra_sleep = (m_format.m_streamInfo.GetDuration() - time_to_add_ms) / 2;
// if there is still place, just add it without blocking
if (m_delay < (m_audiotrackbuffer_sec - (m_format.m_streamInfo.GetDuration() / 1000.0)))
extra_sleep = 0;
if (m_pause_ms > 0.0)
{
extra_sleep = 0;
m_pause_ms -= m_format.m_streamInfo.GetDuration();
if (m_pause_ms <= 0.0)
{
m_pause_ms = 0.0;
CLog::Log(LOGINFO, "Resetting pause bursts as buffer level was reached! (1)");
}
}
usleep(extra_sleep * 1000);
}
else
{
// waiting should only be done if sink is not run dry
double period_time = m_format.m_frames / static_cast<double>(m_sink_sampleRate);
if (m_delay >= (m_audiotrackbuffer_sec - period_time))
{
double time_should_ms = 1000.0 * written_frames / m_format.m_sampleRate;
double time_off = time_should_ms - time_to_add_ms;
if (time_off > 0)
usleep(time_off * 500); // sleep half the error on average away
}
}
return written_frames;
}
void CAESinkAUDIOTRACK::AddPause(unsigned int millis)
{
if (!m_at_jni)
return;
// just sleep out the frames
if (m_at_jni->getPlayState() != CJNIAudioTrack::PLAYSTATE_PAUSED)
m_at_jni->pause();
// This is a mixture to get it right between
// blocking, sleeping roughly and GetDelay smoothing
// In short: Shit in, shit out
usleep(millis * 1000);
m_pause_ms += millis;
}
void CAESinkAUDIOTRACK::Drain()
{
if (!m_at_jni)
return;
CLog::Log(LOGDEBUG, "Draining Audio");
if (IsInitialized())
{
m_at_jni->stop();
// stay ready
m_at_jni->pause();
}
m_duration_written = 0;
m_headPos = 0;
m_timestampPos = 0;
m_linearmovingaverage.clear();
m_stampTimer.SetExpired();
m_pause_ms = 0.0;
}
void CAESinkAUDIOTRACK::Register()
{
AE::AESinkRegEntry entry;
entry.sinkName = "AUDIOTRACK";
entry.createFunc = CAESinkAUDIOTRACK::Create;
entry.enumerateFunc = CAESinkAUDIOTRACK::EnumerateDevicesEx;
AE::CAESinkFactory::RegisterSink(entry);
}
IAESink* CAESinkAUDIOTRACK::Create(std::string &device, AEAudioFormat& desiredFormat)
{
IAESink* sink = new CAESinkAUDIOTRACK();
if (sink->Initialize(desiredFormat, device))
return sink;
delete sink;
return nullptr;
}
void CAESinkAUDIOTRACK::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
{
// Clear everything
m_info.m_channels.Reset();
m_info.m_dataFormats.clear();
m_info.m_sampleRates.clear();
m_info.m_streamTypes.clear();
m_sink_sampleRates.clear();
m_info.m_deviceType = AE_DEVTYPE_PCM;
m_info.m_deviceName = "AudioTrack (IEC)";
m_info.m_displayName = "AudioTrack (IEC)";
m_info.m_displayNameExtra = "Kodi IEC packer (recommended)";
// Query IEC capabilities
bool isRaw = false;
if (CJNIAudioFormat::ENCODING_IEC61937 != -1)
{
UpdateAvailablePCMCapabilities();
UpdateAvailablePassthroughCapabilities(isRaw);
if (!m_info.m_streamTypes.empty())
{
m_info_iec = m_info;
list.push_back(m_info_iec);
m_hasIEC = true;
}
}
// Query RAW capabilities
isRaw = true;
m_info.m_deviceName = "AudioTrack (RAW)";
m_info.m_displayName = "AudioTrack (RAW)";
m_info.m_displayNameExtra = "Android IEC packer";
UpdateAvailablePCMCapabilities();
UpdateAvailablePassthroughCapabilities(isRaw);
m_info_raw = m_info;
// no need to display two PCM sinks - as they are the same
if (!list.empty())
m_info_raw.m_onlyPassthrough = true;
list.push_back(m_info_raw);
}
void CAESinkAUDIOTRACK::UpdateAvailablePassthroughCapabilities(bool isRaw)
{
m_info.m_deviceType = AE_DEVTYPE_HDMI;
m_info.m_wantsIECPassthrough = false;
m_info.m_dataFormats.push_back(AE_FMT_RAW);
m_info.m_streamTypes.clear();
if (isRaw)
{
bool canDoAC3 = false;
if (CJNIAudioFormat::ENCODING_AC3 != -1)
{
if (VerifySinkConfiguration(48000, CJNIAudioFormat::CHANNEL_OUT_STEREO,
CJNIAudioFormat::ENCODING_AC3, true))
{
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_AC3);
CLog::Log(LOGDEBUG, "Firmware implements AC3 RAW");
canDoAC3 = true;
}
}
// EAC3 working on shield, broken on FireTV
if (CJNIAudioFormat::ENCODING_E_AC3 != -1)
{
if (VerifySinkConfiguration(48000, CJNIAudioFormat::CHANNEL_OUT_STEREO,
CJNIAudioFormat::ENCODING_E_AC3, true))
{
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_EAC3);
CLog::Log(LOGDEBUG, "Firmware implements EAC3 RAW");
if (!canDoAC3)
m_passthrough_use_eac3 = true;
}
}
if (CJNIAudioFormat::ENCODING_DTS != -1)
{
if (VerifySinkConfiguration(48000, CJNIAudioFormat::CHANNEL_OUT_STEREO,
CJNIAudioFormat::ENCODING_DTS, true))
{
CLog::Log(LOGDEBUG, "Firmware implements DTS RAW");
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTSHD_CORE);
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTS_1024);
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTS_2048);
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTS_512);
}
}
if (CJNIAudioManager::GetSDKVersion() >= 23)
{
if (CJNIAudioFormat::ENCODING_DTS_HD != -1)
{
if (VerifySinkConfiguration(48000, AEChannelMapToAUDIOTRACKChannelMask(AE_CH_LAYOUT_7_1),
CJNIAudioFormat::ENCODING_DTS_HD, true))
{
CLog::Log(LOGDEBUG, "Firmware implements DTS-HD RAW");
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTSHD);
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTSHD_MA);
}
}
if (CJNIAudioFormat::ENCODING_DOLBY_TRUEHD != -1)
{
if (VerifySinkConfiguration(48000, AEChannelMapToAUDIOTRACKChannelMask(AE_CH_LAYOUT_7_1),
CJNIAudioFormat::ENCODING_DOLBY_TRUEHD, true))
{
CLog::Log(LOGDEBUG, "Firmware implements TrueHD RAW");
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_TRUEHD);
}
}
}
}
else
{
// Android v24 and backports can do real IEC API
if (CJNIAudioFormat::ENCODING_IEC61937 != -1)
{
// check if we support opening an IEC sink at all:
bool supports_iec = VerifySinkConfiguration(48000, CJNIAudioFormat::CHANNEL_OUT_STEREO,
CJNIAudioFormat::ENCODING_IEC61937);
if (supports_iec)
{
bool supports_192khz = m_sink_sampleRates.find(192000) != m_sink_sampleRates.end();
m_info.m_wantsIECPassthrough = true;
m_info.m_streamTypes.clear();
m_info.m_dataFormats.push_back(AE_FMT_RAW);
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_AC3);
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTSHD_CORE);
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTS_1024);
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTS_2048);
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTS_512);
CLog::Log(LOGDEBUG, "AESinkAUDIOTrack: Using IEC PT mode: {}",
CJNIAudioFormat::ENCODING_IEC61937);
CLog::Log(LOGDEBUG, "AC3 and DTS via IEC61937 is supported");
if (supports_192khz)
{
// Check for IEC 2 channel 192 khz PT DTS-HD-HR and E-AC3
if (VerifySinkConfiguration(192000, CJNIAudioFormat::CHANNEL_OUT_STEREO,
CJNIAudioFormat::ENCODING_IEC61937))
{
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_EAC3);
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTSHD);
CLog::Log(LOGDEBUG, "E-AC3 and DTSHD-HR via IEC61937 is supported");
}
// Check for IEC 8 channel 192 khz PT DTS-HD-MA and TrueHD
int atChannelMask = AEChannelMapToAUDIOTRACKChannelMask(AE_CH_LAYOUT_7_1);
if (VerifySinkConfiguration(192000, atChannelMask, CJNIAudioFormat::ENCODING_IEC61937))
{
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_DTSHD_MA);
m_info.m_streamTypes.push_back(CAEStreamInfo::STREAM_TYPE_TRUEHD);
CLog::Log(LOGDEBUG, "DTSHD-MA and TrueHD via IEC61937 is supported");
}
}
}
}
}
}
void CAESinkAUDIOTRACK::UpdateAvailablePCMCapabilities()
{
m_info.m_channels = KnownChannels;
// default fallback format
m_info.m_dataFormats.push_back(AE_FMT_S16LE);
unsigned int native_sampleRate = CJNIAudioTrack::getNativeOutputSampleRate(CJNIAudioManager::STREAM_MUSIC);
m_sink_sampleRates.insert(native_sampleRate);
int encoding = CJNIAudioFormat::ENCODING_PCM_16BIT;
m_sinkSupportsFloat = VerifySinkConfiguration(native_sampleRate, CJNIAudioFormat::CHANNEL_OUT_STEREO, CJNIAudioFormat::ENCODING_PCM_FLOAT);
// Only try for Android 7 or later - there are a lot of old devices that open successfully
// but won't work correctly under the hood (famouse example: old FireTV)
// As even newish devices like Android Chromecast don't do it properly - just disable it ... and use 16 bit Integer
//if (CJNIAudioManager::GetSDKVersion() > 23)
// m_sinkSupportsMultiChannelFloat = VerifySinkConfiguration(native_sampleRate, CJNIAudioFormat::CHANNEL_OUT_7POINT1_SURROUND, CJNIAudioFormat::ENCODING_PCM_FLOAT);
if (m_sinkSupportsFloat)
{
encoding = CJNIAudioFormat::ENCODING_PCM_FLOAT;
m_info.m_dataFormats.push_back(AE_FMT_FLOAT);
CLog::Log(LOGINFO, "Float is supported");
}
if (m_sinkSupportsMultiChannelFloat)
{
CLog::Log(LOGINFO, "Multi channel Float is supported");
}
int test_sample[] = { 32000, 44100, 48000, 88200, 96000, 176400, 192000 };
int test_sample_sz = sizeof(test_sample) / sizeof(int);
for (int i = 0; i < test_sample_sz; ++i)
{
if (IsSupported(test_sample[i], CJNIAudioFormat::CHANNEL_OUT_STEREO, encoding))
{
m_sink_sampleRates.insert(test_sample[i]);
CLog::Log(LOGDEBUG, "AESinkAUDIOTRACK - {} supported", test_sample[i]);
}
}
std::copy(m_sink_sampleRates.begin(), m_sink_sampleRates.end(), std::back_inserter(m_info.m_sampleRates));
}
double CAESinkAUDIOTRACK::GetMovingAverageDelay(double newestdelay)
{
#if defined AT_USE_EXPONENTIAL_AVERAGING
double old = 0.0;
if (m_linearmovingaverage.empty()) // just for creating one space in list
m_linearmovingaverage.push_back(newestdelay);
else
old = m_linearmovingaverage.front();
const double alpha = 0.3;
const double beta = 0.7;
double d = alpha * newestdelay + beta * old;
m_linearmovingaverage.at(0) = d;
return d;
#endif
m_linearmovingaverage.push_back(newestdelay);
// new values are in the back, old values are in the front
// oldest value is removed if elements > MOVING_AVERAGE_MAX_MEMBERS
// removing first element of a vector sucks - I know that
// but hey - 10 elements - not 1 million
size_t size = m_linearmovingaverage.size();
if (size > MOVING_AVERAGE_MAX_MEMBERS)
{
m_linearmovingaverage.pop_front();
size--;
}
// m_{LWMA}^{(n)}(t) = \frac{2}{n (n+1)} \sum_{i=1}^n i \; x(t-n+i)
const double denom = 2.0 / (size * (size + 1));
double sum = 0.0;
for (size_t i = 0; i < m_linearmovingaverage.size(); i++)
sum += (i + 1) * m_linearmovingaverage.at(i);
return sum * denom;
}
|