media_child_ffmpeg_loader.cpp 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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_child_ffmpeg_loader.h"
  8. #include "core/crash_reports.h"
  9. #include "core/file_location.h"
  10. namespace Media {
  11. namespace {
  12. using FFmpeg::AvErrorWrap;
  13. } // namespace
  14. ChildFFMpegLoader::ChildFFMpegLoader(
  15. std::unique_ptr<ExternalSoundData> &&data)
  16. : AbstractAudioFFMpegLoader(
  17. Core::FileLocation(),
  18. QByteArray(),
  19. bytes::vector())
  20. , _parentData(std::move(data)) {
  21. Expects(_parentData->codec != nullptr);
  22. }
  23. bool ChildFFMpegLoader::open(crl::time positionMs, float64 speed) {
  24. const auto sample = (positionMs * samplesFrequency()) / 1000LL;
  25. overrideDuration(sample, _parentData->duration);
  26. return initUsingContext(_parentData->codec.get(), speed);
  27. }
  28. auto ChildFFMpegLoader::readFromInitialFrame() -> ReadResult {
  29. if (!_parentData->frame) {
  30. return ReadError::Wait;
  31. }
  32. return replaceFrameAndRead(base::take(_parentData->frame));
  33. }
  34. auto ChildFFMpegLoader::readMore() -> ReadResult {
  35. if (_readTillEnd) {
  36. return ReadError::EndOfFile;
  37. }
  38. const auto initialFrameResult = readFromInitialFrame();
  39. if (initialFrameResult != ReadError::Wait) {
  40. return initialFrameResult;
  41. }
  42. const auto readResult = readFromReadyContext(
  43. _parentData->codec.get());
  44. if (readResult != ReadError::Wait) {
  45. return readResult;
  46. }
  47. if (_queue.empty()) {
  48. if (!_eofReached) {
  49. return ReadError::Wait;
  50. }
  51. _readTillEnd = true;
  52. return ReadError::EndOfFile;
  53. }
  54. auto packet = std::move(_queue.front());
  55. _queue.pop_front();
  56. _eofReached = packet.empty();
  57. if (_eofReached) {
  58. avcodec_send_packet(_parentData->codec.get(), nullptr); // drain
  59. return ReadError::Retry;
  60. }
  61. AvErrorWrap error = avcodec_send_packet(
  62. _parentData->codec.get(),
  63. &packet.fields());
  64. if (error) {
  65. LogError(u"avcodec_send_packet"_q, error);
  66. // There is a sample voice message where skipping such packet
  67. // results in a crash (read_access to nullptr) in swr_convert().
  68. if (error.code() == AVERROR_INVALIDDATA) {
  69. return ReadError::Retry; // try to skip bad packet
  70. }
  71. return ReadError::Other;
  72. }
  73. return ReadError::Retry;
  74. }
  75. void ChildFFMpegLoader::enqueuePackets(
  76. std::deque<FFmpeg::Packet> &&packets) {
  77. if (_queue.empty()) {
  78. _queue = std::move(packets);
  79. } else {
  80. _queue.insert(
  81. end(_queue),
  82. std::make_move_iterator(packets.begin()),
  83. std::make_move_iterator(packets.end()));
  84. }
  85. packets.clear();
  86. }
  87. void ChildFFMpegLoader::setForceToBuffer(bool force) {
  88. _forceToBuffer = force;
  89. }
  90. bool ChildFFMpegLoader::forceToBuffer() const {
  91. return _forceToBuffer;
  92. }
  93. ChildFFMpegLoader::~ChildFFMpegLoader() = default;
  94. } // namespace Media