media_clip_check_streaming.cpp 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  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/clip/media_clip_check_streaming.h"
  8. #include "core/file_location.h"
  9. #include "base/bytes.h"
  10. #include "logs.h"
  11. #include <QtCore/QtEndian>
  12. #include <QtCore/QBuffer>
  13. #include <QtCore/QFile>
  14. namespace Media {
  15. namespace Clip {
  16. namespace {
  17. constexpr auto kHeaderSize = 8;
  18. constexpr auto kFindMoovBefore = 128 * 1024;
  19. template <typename Type>
  20. Type ReadBigEndian(bytes::const_span data) {
  21. const auto bytes = data.subspan(0, sizeof(Type)).data();
  22. return qFromBigEndian(*reinterpret_cast<const Type*>(bytes));
  23. }
  24. bool IsAtom(bytes::const_span header, const char (&atom)[5]) {
  25. return bytes::compare(
  26. header.subspan(4, 4),
  27. bytes::make_span(atom).subspan(0, 4)) == 0;
  28. }
  29. } // namespace
  30. bool CheckStreamingSupport(
  31. const Core::FileLocation &location,
  32. QByteArray data) {
  33. QBuffer buffer;
  34. QFile file;
  35. if (data.isEmpty()) {
  36. file.setFileName(location.name());
  37. } else {
  38. buffer.setBuffer(&data);
  39. }
  40. const auto size = data.isEmpty()
  41. ? file.size()
  42. : data.size();
  43. const auto device = data.isEmpty()
  44. ? static_cast<QIODevice*>(&file)
  45. : static_cast<QIODevice*>(&buffer);
  46. if (size < kHeaderSize || !device->open(QIODevice::ReadOnly)) {
  47. return false;
  48. }
  49. auto lastReadPosition = 0;
  50. char atomHeader[kHeaderSize] = { 0 };
  51. auto atomHeaderBytes = bytes::make_span(atomHeader);
  52. while (true) {
  53. const auto position = device->pos();
  54. if (device->read(atomHeader, kHeaderSize) != kHeaderSize) {
  55. break;
  56. }
  57. if (lastReadPosition >= kFindMoovBefore) {
  58. return false;
  59. } else if (IsAtom(atomHeaderBytes, "moov")) {
  60. return true;
  61. }
  62. const auto length = [&] {
  63. const auto result = ReadBigEndian<uint32>(atomHeaderBytes);
  64. if (result != 1) {
  65. return uint64(result);
  66. }
  67. char atomSize64[kHeaderSize] = { 0 };
  68. if (device->read(atomSize64, kHeaderSize) != kHeaderSize) {
  69. return uint64(-1);
  70. }
  71. auto atomSize64Bytes = bytes::make_span(atomSize64);
  72. return ReadBigEndian<uint64>(atomSize64Bytes);
  73. }();
  74. if (position + length > size) {
  75. break;
  76. }
  77. device->seek(position + length);
  78. lastReadPosition = position;
  79. }
  80. return false;
  81. }
  82. } // namespace Clip
  83. } // namespace Media