media_audio_local_cache.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "media/audio/media_audio_local_cache.h"
  8. #include "ffmpeg/ffmpeg_bytes_io_wrap.h"
  9. #include "ffmpeg/ffmpeg_utility.h"
  10. namespace Media::Audio {
  11. namespace {
  12. constexpr auto kMaxDuration = 3 * crl::time(1000);
  13. constexpr auto kMaxStreams = 2;
  14. constexpr auto kFrameSize = 4096;
  15. [[nodiscard]] QByteArray ConvertAndCut(const QByteArray &bytes) {
  16. using namespace FFmpeg;
  17. if (bytes.isEmpty()) {
  18. return {};
  19. }
  20. auto wrap = ReadBytesWrap{
  21. .size = bytes.size(),
  22. .data = reinterpret_cast<const uchar*>(bytes.constData()),
  23. };
  24. auto input = MakeFormatPointer(
  25. &wrap,
  26. &ReadBytesWrap::Read,
  27. nullptr,
  28. &ReadBytesWrap::Seek);
  29. if (!input) {
  30. return {};
  31. }
  32. auto error = AvErrorWrap(avformat_find_stream_info(input.get(), 0));
  33. if (error) {
  34. LogError(u"avformat_find_stream_info"_q, error);
  35. return {};
  36. }
  37. #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 79, 100)
  38. auto inCodec = (const AVCodec*)nullptr;
  39. #else
  40. auto inCodec = (AVCodec*)nullptr;
  41. #endif
  42. const auto streamId = av_find_best_stream(
  43. input.get(),
  44. AVMEDIA_TYPE_AUDIO,
  45. -1,
  46. -1,
  47. &inCodec,
  48. 0);
  49. if (streamId < 0) {
  50. LogError(u"av_find_best_stream"_q, AvErrorWrap(streamId));
  51. return {};
  52. }
  53. auto inStream = input->streams[streamId];
  54. auto inCodecPar = inStream->codecpar;
  55. auto inCodecContext = CodecPointer(avcodec_alloc_context3(nullptr));
  56. if (!inCodecContext) {
  57. return {};
  58. }
  59. if (avcodec_parameters_to_context(inCodecContext.get(), inCodecPar) < 0) {
  60. return {};
  61. }
  62. if (avcodec_open2(inCodecContext.get(), inCodec, nullptr) < 0) {
  63. return {};
  64. }
  65. auto result = WriteBytesWrap();
  66. auto outFormat = MakeWriteFormatPointer(
  67. static_cast<void*>(&result),
  68. nullptr,
  69. &WriteBytesWrap::Write,
  70. &WriteBytesWrap::Seek,
  71. "wav"_q);
  72. if (!outFormat) {
  73. return {};
  74. }
  75. // Find and open output codec
  76. auto outCodec = avcodec_find_encoder(AV_CODEC_ID_PCM_S16LE);
  77. if (!outCodec) {
  78. return {};
  79. }
  80. auto outStream = avformat_new_stream(outFormat.get(), outCodec);
  81. if (!outStream) {
  82. return {};
  83. }
  84. auto outCodecContext = CodecPointer(
  85. avcodec_alloc_context3(outCodec));
  86. if (!outCodecContext) {
  87. return {};
  88. }
  89. #if DA_FFMPEG_NEW_CHANNEL_LAYOUT
  90. auto mono = AVChannelLayout(AV_CHANNEL_LAYOUT_MONO);
  91. auto stereo = AVChannelLayout(AV_CHANNEL_LAYOUT_STEREO);
  92. const auto in = &inCodecContext->ch_layout;
  93. if (!av_channel_layout_compare(in, &mono)
  94. || !av_channel_layout_compare(in, &stereo)) {
  95. av_channel_layout_copy(&outCodecContext->ch_layout, in);
  96. } else {
  97. outCodecContext->ch_layout = AV_CHANNEL_LAYOUT_STEREO;
  98. }
  99. #else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
  100. const auto in = inCodecContext->channels;
  101. if (in == 1 || in == 2) {
  102. outCodecContext->channels = in;
  103. outCodecContext->channel_layout = inCodecContext->channel_layout;
  104. } else {
  105. outCodecContext->channels = 2;
  106. outCodecContext->channel_layout = AV_CH_LAYOUT_STEREO;
  107. }
  108. #endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
  109. const auto rate = 44'100;
  110. outCodecContext->sample_fmt = AV_SAMPLE_FMT_S16;
  111. outCodecContext->time_base = AVRational{ 1, rate };
  112. outCodecContext->sample_rate = rate;
  113. error = avcodec_open2(outCodecContext.get(), outCodec, nullptr);
  114. if (error) {
  115. LogError("avcodec_open2", error);
  116. return {};
  117. }
  118. error = avcodec_parameters_from_context(
  119. outStream->codecpar,
  120. outCodecContext.get());
  121. if (error) {
  122. LogError("avcodec_parameters_from_context", error);
  123. return {};
  124. }
  125. error = avformat_write_header(outFormat.get(), nullptr);
  126. if (error) {
  127. LogError("avformat_write_header", error);
  128. return {};
  129. }
  130. auto swrContext = MakeSwresamplePointer(
  131. #if DA_FFMPEG_NEW_CHANNEL_LAYOUT
  132. &inCodecContext->ch_layout,
  133. inCodecContext->sample_fmt,
  134. inCodecContext->sample_rate,
  135. &outCodecContext->ch_layout,
  136. #else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
  137. inCodecContext->channel_layout,
  138. inCodecContext->sample_fmt,
  139. inCodecContext->sample_rate,
  140. outCodecContext->channel_layout,
  141. #endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
  142. outCodecContext->sample_fmt,
  143. outCodecContext->sample_rate);
  144. if (!swrContext) {
  145. return {};
  146. }
  147. auto packet = av_packet_alloc();
  148. const auto guard = gsl::finally([&] {
  149. av_packet_free(&packet);
  150. });
  151. auto frame = MakeFramePointer();
  152. if (!frame) {
  153. return {};
  154. }
  155. auto outFrame = MakeFramePointer();
  156. if (!outFrame) {
  157. return {};
  158. }
  159. outFrame->nb_samples = kFrameSize;
  160. outFrame->format = outCodecContext->sample_fmt;
  161. #if DA_FFMPEG_NEW_CHANNEL_LAYOUT
  162. av_channel_layout_copy(
  163. &outFrame->ch_layout,
  164. &outCodecContext->ch_layout);
  165. #else // DA_FFMPEG_NEW_CHANNEL_LAYOUT
  166. outFrame->channel_layout = outCodecContext->channel_layout;
  167. outFrame->channels = outCodecContext->channels;
  168. #endif // DA_FFMPEG_NEW_CHANNEL_LAYOUT
  169. outFrame->sample_rate = outCodecContext->sample_rate;
  170. error = av_frame_get_buffer(outFrame.get(), 0);
  171. if (error) {
  172. LogError("av_frame_get_buffer", error);
  173. return {};
  174. }
  175. auto pts = int64_t(0);
  176. auto maxPts = int64_t(kMaxDuration) * rate / 1000;
  177. const auto writeFrame = [&](AVFrame *frame) { // nullptr to flush
  178. error = avcodec_send_frame(outCodecContext.get(), frame);
  179. if (error) {
  180. LogError("avcodec_send_frame", error);
  181. return error;
  182. }
  183. auto pkt = av_packet_alloc();
  184. const auto guard = gsl::finally([&] {
  185. av_packet_free(&pkt);
  186. });
  187. while (true) {
  188. error = avcodec_receive_packet(outCodecContext.get(), pkt);
  189. if (error) {
  190. if (error.code() != AVERROR(EAGAIN)
  191. && error.code() != AVERROR_EOF) {
  192. LogError("avcodec_receive_packet", error);
  193. }
  194. return error;
  195. }
  196. pkt->stream_index = outStream->index;
  197. av_packet_rescale_ts(
  198. pkt,
  199. outCodecContext->time_base,
  200. outStream->time_base);
  201. error = av_interleaved_write_frame(outFormat.get(), pkt);
  202. if (error) {
  203. LogError("av_interleaved_write_frame", error);
  204. return error;
  205. }
  206. }
  207. };
  208. while (pts < maxPts) {
  209. error = av_read_frame(input.get(), packet);
  210. const auto finished = (error.code() == AVERROR_EOF);
  211. if (!finished) {
  212. if (error) {
  213. LogError("av_read_frame", error);
  214. return {};
  215. }
  216. auto guard = gsl::finally([&] {
  217. av_packet_unref(packet);
  218. });
  219. if (packet->stream_index != streamId) {
  220. continue;
  221. }
  222. error = avcodec_send_packet(inCodecContext.get(), packet);
  223. if (error) {
  224. LogError("avcodec_send_packet", error);
  225. return {};
  226. }
  227. }
  228. while (true) {
  229. error = avcodec_receive_frame(inCodecContext.get(), frame.get());
  230. if (error) {
  231. if (error.code() == AVERROR(EAGAIN)
  232. || error.code() == AVERROR_EOF) {
  233. break;
  234. } else {
  235. LogError("avcodec_receive_frame", error);
  236. return {};
  237. }
  238. }
  239. error = swr_convert(
  240. swrContext.get(),
  241. outFrame->data,
  242. kFrameSize,
  243. (const uint8_t**)frame->data,
  244. frame->nb_samples);
  245. if (error) {
  246. LogError("swr_convert", error);
  247. return {};
  248. }
  249. const auto samples = error.code();
  250. if (!samples) {
  251. continue;
  252. }
  253. outFrame->nb_samples = samples;
  254. outFrame->pts = pts;
  255. pts += samples;
  256. if (pts > maxPts) {
  257. break;
  258. }
  259. error = writeFrame(outFrame.get());
  260. if (error && error.code() != AVERROR(EAGAIN)) {
  261. return {};
  262. }
  263. }
  264. if (finished) {
  265. break;
  266. }
  267. }
  268. error = writeFrame(nullptr);
  269. if (error && error.code() != AVERROR_EOF) {
  270. return {};
  271. }
  272. error = av_write_trailer(outFormat.get());
  273. if (error) {
  274. LogError("av_write_trailer", error);
  275. return {};
  276. }
  277. return result.content;
  278. }
  279. } // namespace
  280. LocalSound LocalCache::sound(
  281. DocumentId id,
  282. Fn<QByteArray()> resolveOriginalBytes,
  283. Fn<QByteArray()> fallbackOriginalBytes) {
  284. auto &result = _cache[id];
  285. if (!result.isEmpty()) {
  286. return { id, result };
  287. }
  288. result = ConvertAndCut(resolveOriginalBytes());
  289. return !result.isEmpty()
  290. ? LocalSound{ id, result }
  291. : fallbackOriginalBytes
  292. ? sound(0, fallbackOriginalBytes, nullptr)
  293. : LocalSound();
  294. }
  295. LocalDiskCache::LocalDiskCache(const QString &folder)
  296. : _base(folder + '/') {
  297. QDir().mkpath(_base);
  298. }
  299. QString LocalDiskCache::name(const LocalSound &sound) {
  300. if (!sound) {
  301. return {};
  302. }
  303. const auto i = _paths.find(sound.id);
  304. if (i != end(_paths)) {
  305. return i->second;
  306. }
  307. auto result = u"TD_%1"_q.arg(sound.id
  308. ? QString::number(sound.id, 16).toUpper()
  309. : u"Default"_q);
  310. const auto path = _base + u"%1.wav"_q.arg(result);
  311. auto f = QFile(path);
  312. if (f.open(QIODevice::WriteOnly)) {
  313. f.write(sound.wav);
  314. f.close();
  315. }
  316. _paths.emplace(sound.id, result);
  317. return result;
  318. }
  319. QString LocalDiskCache::path(const LocalSound &sound) {
  320. const auto part = name(sound);
  321. return part.isEmpty() ? QString() : _base + part + u".wav"_q;
  322. }
  323. } // namespace Media::Audio