NBAIO_Tee.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. /*
  2. * Copyright (C) 2018 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. #define LOG_TAG "NBAIO_Tee"
  17. //#define LOG_NDEBUG 0
  18. #include <utils/Log.h>
  19. #include <deque>
  20. #include <dirent.h>
  21. #include <future>
  22. #include <list>
  23. #include <vector>
  24. #include <audio_utils/format.h>
  25. #include <audio_utils/sndfile.h>
  26. #include <media/nbaio/PipeReader.h>
  27. #include "Configuration.h"
  28. #include "NBAIO_Tee.h"
  29. // Enabled with TEE_SINK in Configuration.h
  30. #ifdef TEE_SINK
  31. namespace android {
  32. /*
  33. Tee filenames generated as follows:
  34. "aftee_Date_ThreadId_C_reason.wav" RecordThread
  35. "aftee_Date_ThreadId_M_reason.wav" MixerThread (Normal)
  36. "aftee_Date_ThreadId_F_reason.wav" MixerThread (Fast)
  37. "aftee_Date_ThreadId_TrackId_R_reason.wav" RecordTrack
  38. "aftee_Date_ThreadId_TrackId_TrackName_T_reason.wav" PlaybackTrack
  39. where Date = YYYYmmdd_HHMMSS_MSEC
  40. where Reason = [ DTOR | DUMP | REMOVE ]
  41. Examples:
  42. aftee_20180424_153811_038_13_57_2_T_REMOVE.wav
  43. aftee_20180424_153811_218_13_57_2_T_REMOVE.wav
  44. aftee_20180424_153811_378_13_57_2_T_REMOVE.wav
  45. aftee_20180424_153825_147_62_C_DUMP.wav
  46. aftee_20180424_153825_148_62_59_R_DUMP.wav
  47. aftee_20180424_153825_149_13_F_DUMP.wav
  48. aftee_20180424_153842_125_62_59_R_REMOVE.wav
  49. aftee_20180424_153842_168_62_C_DTOR.wav
  50. */
  51. static constexpr char DEFAULT_PREFIX[] = "aftee_";
  52. static constexpr char DEFAULT_DIRECTORY[] = "/data/misc/audioserver";
  53. static constexpr size_t DEFAULT_THREADPOOL_SIZE = 8;
  54. /** AudioFileHandler manages temporary audio wav files with a least recently created
  55. retention policy.
  56. The temporary filenames are systematically generated. A common filename prefix,
  57. storage directory, and concurrency pool are passed in on creating the object.
  58. Temporary files are created by "create", which returns a filename generated by
  59. prefix + 14 char date + suffix
  60. TODO Move to audio_utils.
  61. TODO Avoid pointing two AudioFileHandlers to the same directory and prefix
  62. as we don't have a prefix specific lock file. */
  63. class AudioFileHandler {
  64. public:
  65. AudioFileHandler(const std::string &prefix, const std::string &directory, size_t pool)
  66. : mThreadPool(pool)
  67. , mPrefix(prefix)
  68. {
  69. (void)setDirectory(directory);
  70. }
  71. /** returns filename of created audio file, else empty string on failure. */
  72. std::string create(
  73. std::function<ssize_t /* frames_read */
  74. (void * /* buffer */, size_t /* size_in_frames */)> reader,
  75. uint32_t sampleRate,
  76. uint32_t channelCount,
  77. audio_format_t format,
  78. const std::string &suffix);
  79. private:
  80. /** sets the current directory. this is currently private to avoid confusion
  81. when changing while pending operations are occurring (it's okay, but
  82. weakly synchronized). */
  83. status_t setDirectory(const std::string &directory);
  84. /** cleans current directory and returns the directory name done. */
  85. status_t clean(std::string *dir = nullptr);
  86. /** creates an audio file from a reader functor passed in. */
  87. status_t createInternal(
  88. std::function<ssize_t /* frames_read */
  89. (void * /* buffer */, size_t /* size_in_frames */)> reader,
  90. uint32_t sampleRate,
  91. uint32_t channelCount,
  92. audio_format_t format,
  93. const std::string &filename);
  94. static bool isDirectoryValid(const std::string &directory) {
  95. return directory.size() > 0 && directory[0] == '/';
  96. }
  97. std::string generateFilename(const std::string &suffix) const {
  98. char fileTime[sizeof("YYYYmmdd_HHMMSS_\0")];
  99. struct timeval tv;
  100. gettimeofday(&tv, NULL);
  101. struct tm tm;
  102. localtime_r(&tv.tv_sec, &tm);
  103. LOG_ALWAYS_FATAL_IF(strftime(fileTime, sizeof(fileTime), "%Y%m%d_%H%M%S_", &tm) == 0,
  104. "incorrect fileTime buffer");
  105. char msec[4];
  106. (void)snprintf(msec, sizeof(msec), "%03d", (int)(tv.tv_usec / 1000));
  107. return mPrefix + fileTime + msec + suffix + ".wav";
  108. }
  109. bool isManagedFilename(const char *name) {
  110. constexpr size_t FILENAME_LEN_DATE = 4 + 2 + 2 // %Y%m%d%
  111. + 1 + 2 + 2 + 2 // _H%M%S
  112. + 1 + 3; //_MSEC
  113. const size_t prefixLen = mPrefix.size();
  114. const size_t nameLen = strlen(name);
  115. // reject on size, prefix, and .wav
  116. if (nameLen < prefixLen + FILENAME_LEN_DATE + 4 /* .wav */
  117. || strncmp(name, mPrefix.c_str(), prefixLen) != 0
  118. || strcmp(name + nameLen - 4, ".wav") != 0) {
  119. return false;
  120. }
  121. // validate date portion
  122. const char *date = name + prefixLen;
  123. return std::all_of(date, date + 8, isdigit)
  124. && date[8] == '_'
  125. && std::all_of(date + 9, date + 15, isdigit)
  126. && date[15] == '_'
  127. && std::all_of(date + 16, date + 19, isdigit);
  128. }
  129. // yet another ThreadPool implementation.
  130. class ThreadPool {
  131. public:
  132. ThreadPool(size_t size)
  133. : mThreadPoolSize(size)
  134. { }
  135. /** launches task "name" with associated function "func".
  136. if the threadpool is exhausted, it will launch on calling function */
  137. status_t launch(const std::string &name, std::function<status_t()> func);
  138. private:
  139. std::mutex mLock;
  140. std::list<std::pair<
  141. std::string, std::future<status_t>>> mFutures; // GUARDED_BY(mLock)
  142. const size_t mThreadPoolSize;
  143. } mThreadPool;
  144. const std::string mPrefix;
  145. std::mutex mLock;
  146. std::string mDirectory; // GUARDED_BY(mLock)
  147. std::deque<std::string> mFiles; // GUARDED_BY(mLock) sorted list of files by creation time
  148. static constexpr size_t FRAMES_PER_READ = 1024;
  149. static constexpr size_t MAX_FILES_READ = 1024;
  150. static constexpr size_t MAX_FILES_KEEP = 32;
  151. };
  152. /* static */
  153. void NBAIO_Tee::NBAIO_TeeImpl::dumpTee(
  154. int fd, const NBAIO_SinkSource &sinkSource, const std::string &suffix)
  155. {
  156. // Singleton. Constructed thread-safe on first call, never destroyed.
  157. static AudioFileHandler audioFileHandler(
  158. DEFAULT_PREFIX, DEFAULT_DIRECTORY, DEFAULT_THREADPOOL_SIZE);
  159. auto &source = sinkSource.second;
  160. if (source.get() == nullptr) {
  161. return;
  162. }
  163. const NBAIO_Format format = source->format();
  164. bool firstRead = true;
  165. std::string filename = audioFileHandler.create(
  166. // this functor must not hold references to stack
  167. [firstRead, sinkSource] (void *buffer, size_t frames) mutable {
  168. auto &source = sinkSource.second;
  169. ssize_t actualRead = source->read(buffer, frames);
  170. if (actualRead == (ssize_t)OVERRUN && firstRead) {
  171. // recheck once
  172. actualRead = source->read(buffer, frames);
  173. }
  174. firstRead = false;
  175. return actualRead;
  176. },
  177. Format_sampleRate(format),
  178. Format_channelCount(format),
  179. format.mFormat,
  180. suffix);
  181. if (fd >= 0 && filename.size() > 0) {
  182. dprintf(fd, "tee wrote to %s\n", filename.c_str());
  183. }
  184. }
  185. /* static */
  186. NBAIO_Tee::NBAIO_TeeImpl::NBAIO_SinkSource NBAIO_Tee::NBAIO_TeeImpl::makeSinkSource(
  187. const NBAIO_Format &format, size_t frames, bool *enabled)
  188. {
  189. if (Format_isValid(format) && audio_is_linear_pcm(format.mFormat)) {
  190. Pipe *pipe = new Pipe(frames, format);
  191. size_t numCounterOffers = 0;
  192. const NBAIO_Format offers[1] = {format};
  193. ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
  194. if (index != 0) {
  195. ALOGW("pipe failure to negotiate: %zd", index);
  196. goto exit;
  197. }
  198. PipeReader *pipeReader = new PipeReader(*pipe);
  199. numCounterOffers = 0;
  200. index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
  201. if (index != 0) {
  202. ALOGW("pipeReader failure to negotiate: %zd", index);
  203. goto exit;
  204. }
  205. if (enabled != nullptr) *enabled = true;
  206. return {pipe, pipeReader};
  207. }
  208. exit:
  209. if (enabled != nullptr) *enabled = false;
  210. return {nullptr, nullptr};
  211. }
  212. std::string AudioFileHandler::create(
  213. std::function<ssize_t /* frames_read */
  214. (void * /* buffer */, size_t /* size_in_frames */)> reader,
  215. uint32_t sampleRate,
  216. uint32_t channelCount,
  217. audio_format_t format,
  218. const std::string &suffix)
  219. {
  220. const std::string filename = generateFilename(suffix);
  221. if (mThreadPool.launch(std::string("create ") + filename,
  222. [=]() { return createInternal(reader, sampleRate, channelCount, format, filename); })
  223. == NO_ERROR) {
  224. return filename;
  225. }
  226. return "";
  227. }
  228. status_t AudioFileHandler::setDirectory(const std::string &directory)
  229. {
  230. if (!isDirectoryValid(directory)) return BAD_VALUE;
  231. // TODO: consider using std::filesystem in C++17
  232. DIR *dir = opendir(directory.c_str());
  233. if (dir == nullptr) {
  234. ALOGW("%s: cannot open directory %s", __func__, directory.c_str());
  235. return BAD_VALUE;
  236. }
  237. size_t toRemove = 0;
  238. decltype(mFiles) files;
  239. while (files.size() < MAX_FILES_READ) {
  240. errno = 0;
  241. const struct dirent *result = readdir(dir);
  242. if (result == nullptr) {
  243. ALOGW_IF(errno != 0, "%s: readdir failure %s", __func__, strerror(errno));
  244. break;
  245. }
  246. // is it a managed filename?
  247. if (!isManagedFilename(result->d_name)) {
  248. continue;
  249. }
  250. files.emplace_back(result->d_name);
  251. }
  252. (void)closedir(dir);
  253. // OPTIMIZATION: we don't need to stat each file, the filenames names are
  254. // already (roughly) ordered by creation date. we use std::deque instead
  255. // of std::set for faster insertion and sorting times.
  256. if (files.size() > MAX_FILES_KEEP) {
  257. // removed files can use a partition (no need to do a full sort).
  258. toRemove = files.size() - MAX_FILES_KEEP;
  259. std::nth_element(files.begin(), files.begin() + toRemove - 1, files.end());
  260. }
  261. // kept files must be sorted.
  262. std::sort(files.begin() + toRemove, files.end());
  263. {
  264. std::lock_guard<std::mutex> _l(mLock);
  265. mDirectory = directory;
  266. mFiles = std::move(files);
  267. }
  268. if (toRemove > 0) { // launch a clean in background.
  269. (void)mThreadPool.launch(
  270. std::string("cleaning ") + directory, [this]() { return clean(); });
  271. }
  272. return NO_ERROR;
  273. }
  274. status_t AudioFileHandler::clean(std::string *directory)
  275. {
  276. std::vector<std::string> filesToRemove;
  277. std::string dir;
  278. {
  279. std::lock_guard<std::mutex> _l(mLock);
  280. if (!isDirectoryValid(mDirectory)) return NO_INIT;
  281. dir = mDirectory;
  282. if (mFiles.size() > MAX_FILES_KEEP) {
  283. size_t toRemove = mFiles.size() - MAX_FILES_KEEP;
  284. // use move and erase to efficiently transfer std::string
  285. std::move(mFiles.begin(),
  286. mFiles.begin() + toRemove,
  287. std::back_inserter(filesToRemove));
  288. mFiles.erase(mFiles.begin(), mFiles.begin() + toRemove);
  289. }
  290. }
  291. std::string dirp = dir + "/";
  292. // remove files outside of lock for better concurrency.
  293. for (const auto &file : filesToRemove) {
  294. (void)unlink((dirp + file).c_str());
  295. }
  296. // return the directory if requested.
  297. if (directory != nullptr) {
  298. *directory = dir;
  299. }
  300. return NO_ERROR;
  301. }
  302. status_t AudioFileHandler::ThreadPool::launch(
  303. const std::string &name, std::function<status_t()> func)
  304. {
  305. if (mThreadPoolSize > 1) {
  306. std::lock_guard<std::mutex> _l(mLock);
  307. if (mFutures.size() >= mThreadPoolSize) {
  308. for (auto it = mFutures.begin(); it != mFutures.end();) {
  309. const std::string &filename = it->first;
  310. std::future<status_t> &future = it->second;
  311. if (!future.valid() ||
  312. future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
  313. ALOGV("%s: future %s ready", __func__, filename.c_str());
  314. it = mFutures.erase(it);
  315. } else {
  316. ALOGV("%s: future %s not ready", __func__, filename.c_str());
  317. ++it;
  318. }
  319. }
  320. }
  321. if (mFutures.size() < mThreadPoolSize) {
  322. ALOGV("%s: deferred calling %s", __func__, name.c_str());
  323. mFutures.emplace_back(name, std::async(std::launch::async, func));
  324. return NO_ERROR;
  325. }
  326. }
  327. ALOGV("%s: immediate calling %s", __func__, name.c_str());
  328. return func();
  329. }
  330. status_t AudioFileHandler::createInternal(
  331. std::function<ssize_t /* frames_read */
  332. (void * /* buffer */, size_t /* size_in_frames */)> reader,
  333. uint32_t sampleRate,
  334. uint32_t channelCount,
  335. audio_format_t format,
  336. const std::string &filename)
  337. {
  338. // Attempt to choose the best matching file format.
  339. // We can choose any sf_format
  340. // but writeFormat must be one of 16, 32, float
  341. // due to sf_writef compatibility.
  342. int sf_format;
  343. audio_format_t writeFormat;
  344. switch (format) {
  345. case AUDIO_FORMAT_PCM_8_BIT:
  346. case AUDIO_FORMAT_PCM_16_BIT:
  347. sf_format = SF_FORMAT_PCM_16;
  348. writeFormat = AUDIO_FORMAT_PCM_16_BIT;
  349. ALOGV("%s: %s using PCM_16 for format %#x", __func__, filename.c_str(), format);
  350. break;
  351. case AUDIO_FORMAT_PCM_8_24_BIT:
  352. case AUDIO_FORMAT_PCM_24_BIT_PACKED:
  353. case AUDIO_FORMAT_PCM_32_BIT:
  354. sf_format = SF_FORMAT_PCM_32;
  355. writeFormat = AUDIO_FORMAT_PCM_32_BIT;
  356. ALOGV("%s: %s using PCM_32 for format %#x", __func__, filename.c_str(), format);
  357. break;
  358. case AUDIO_FORMAT_PCM_FLOAT:
  359. sf_format = SF_FORMAT_FLOAT;
  360. writeFormat = AUDIO_FORMAT_PCM_FLOAT;
  361. ALOGV("%s: %s using PCM_FLOAT for format %#x", __func__, filename.c_str(), format);
  362. break;
  363. default:
  364. // TODO:
  365. // handle audio_has_proportional_frames() formats.
  366. // handle compressed formats as single byte files.
  367. return BAD_VALUE;
  368. }
  369. std::string directory;
  370. status_t status = clean(&directory);
  371. if (status != NO_ERROR) return status;
  372. std::string dirPrefix = directory + "/";
  373. const std::string path = dirPrefix + filename;
  374. /* const */ SF_INFO info = {
  375. .frames = 0,
  376. .samplerate = (int)sampleRate,
  377. .channels = (int)channelCount,
  378. .format = SF_FORMAT_WAV | sf_format,
  379. };
  380. SNDFILE *sf = sf_open(path.c_str(), SFM_WRITE, &info);
  381. if (sf == nullptr) {
  382. return INVALID_OPERATION;
  383. }
  384. size_t total = 0;
  385. void *buffer = malloc(FRAMES_PER_READ * std::max(
  386. channelCount * audio_bytes_per_sample(writeFormat), //output framesize
  387. channelCount * audio_bytes_per_sample(format))); // input framesize
  388. if (buffer == nullptr) {
  389. sf_close(sf);
  390. return NO_MEMORY;
  391. }
  392. for (;;) {
  393. const ssize_t actualRead = reader(buffer, FRAMES_PER_READ);
  394. if (actualRead <= 0) {
  395. break;
  396. }
  397. // Convert input format to writeFormat as needed.
  398. if (format != writeFormat) {
  399. memcpy_by_audio_format(
  400. buffer, writeFormat, buffer, format, actualRead * info.channels);
  401. }
  402. ssize_t reallyWritten;
  403. switch (writeFormat) {
  404. case AUDIO_FORMAT_PCM_16_BIT:
  405. reallyWritten = sf_writef_short(sf, (const int16_t *)buffer, actualRead);
  406. break;
  407. case AUDIO_FORMAT_PCM_32_BIT:
  408. reallyWritten = sf_writef_int(sf, (const int32_t *)buffer, actualRead);
  409. break;
  410. case AUDIO_FORMAT_PCM_FLOAT:
  411. reallyWritten = sf_writef_float(sf, (const float *)buffer, actualRead);
  412. break;
  413. default:
  414. LOG_ALWAYS_FATAL("%s: %s writeFormat: %#x", __func__, filename.c_str(), writeFormat);
  415. break;
  416. }
  417. if (reallyWritten < 0) {
  418. ALOGW("%s: %s write error: %zd", __func__, filename.c_str(), reallyWritten);
  419. break;
  420. }
  421. total += reallyWritten;
  422. if (reallyWritten < actualRead) {
  423. ALOGW("%s: %s write short count: %zd < %zd",
  424. __func__, filename.c_str(), reallyWritten, actualRead);
  425. break;
  426. }
  427. }
  428. sf_close(sf);
  429. free(buffer);
  430. if (total == 0) {
  431. (void)unlink(path.c_str());
  432. return NOT_ENOUGH_DATA;
  433. }
  434. // Success: add our name to managed files.
  435. {
  436. std::lock_guard<std::mutex> _l(mLock);
  437. // weak synchronization - only update mFiles if the directory hasn't changed.
  438. if (mDirectory == directory) {
  439. mFiles.emplace_back(filename); // add to the end to preserve sort.
  440. }
  441. }
  442. return NO_ERROR; // return full path
  443. }
  444. } // namespace android
  445. #endif // TEE_SINK