1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "media/clip/media_clip_check_streaming.h"
9
10 #include "core/file_location.h"
11 #include "base/bytes.h"
12 #include "logs.h"
13
14 #include <QtCore/QtEndian>
15 #include <QtCore/QBuffer>
16 #include <QtCore/QFile>
17
18 namespace Media {
19 namespace Clip {
20 namespace {
21
22 constexpr auto kHeaderSize = 8;
23 constexpr auto kFindMoovBefore = 128 * 1024;
24
25 template <typename Type>
ReadBigEndian(bytes::const_span data)26 Type ReadBigEndian(bytes::const_span data) {
27 const auto bytes = data.subspan(0, sizeof(Type)).data();
28 return qFromBigEndian(*reinterpret_cast<const Type*>(bytes));
29 }
30
IsAtom(bytes::const_span header,const char (& atom)[5])31 bool IsAtom(bytes::const_span header, const char (&atom)[5]) {
32 return bytes::compare(
33 header.subspan(4, 4),
34 bytes::make_span(atom).subspan(0, 4)) == 0;
35 }
36
37 } // namespace
38
CheckStreamingSupport(const Core::FileLocation & location,QByteArray data)39 bool CheckStreamingSupport(
40 const Core::FileLocation &location,
41 QByteArray data) {
42 QBuffer buffer;
43 QFile file;
44 if (data.isEmpty()) {
45 file.setFileName(location.name());
46 } else {
47 buffer.setBuffer(&data);
48 }
49 const auto size = data.isEmpty()
50 ? file.size()
51 : data.size();
52 const auto device = data.isEmpty()
53 ? static_cast<QIODevice*>(&file)
54 : static_cast<QIODevice*>(&buffer);
55
56 if (size < kHeaderSize || !device->open(QIODevice::ReadOnly)) {
57 return false;
58 }
59
60 auto lastReadPosition = 0;
61 char atomHeader[kHeaderSize] = { 0 };
62 auto atomHeaderBytes = bytes::make_span(atomHeader);
63 while (true) {
64 const auto position = device->pos();
65 if (device->read(atomHeader, kHeaderSize) != kHeaderSize) {
66 break;
67 }
68
69 if (lastReadPosition >= kFindMoovBefore) {
70 return false;
71 } else if (IsAtom(atomHeaderBytes, "moov")) {
72 return true;
73 }
74
75 const auto length = [&] {
76 const auto result = ReadBigEndian<uint32>(atomHeaderBytes);
77 if (result != 1) {
78 return uint64(result);
79 }
80 char atomSize64[kHeaderSize] = { 0 };
81 if (device->read(atomSize64, kHeaderSize) != kHeaderSize) {
82 return uint64(-1);
83 }
84 auto atomSize64Bytes = bytes::make_span(atomSize64);
85 return ReadBigEndian<uint64>(atomSize64Bytes);
86 }();
87 if (position + length > size) {
88 break;
89 }
90 device->seek(position + length);
91 lastReadPosition = position;
92 }
93 return false;
94 }
95
96 } // namespace Clip
97 } // namespace Media
98