1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
#include "seekable_buffer.h"
#include <c10/util/Logging.h>
#include <chrono>
#include "memory_buffer.h"
namespace ffmpeg {
int SeekableBuffer::init(
DecoderInCallback&& in,
uint64_t timeoutMs,
size_t maxSeekableBytes,
ImageType* type) {
shutdown();
isSeekable_ = in(nullptr, 0, 0, 0) == 0;
if (isSeekable_) { // seekable
if (type) {
if (!readBytes(in, 8, timeoutMs)) {
return -1;
}
setImageType(type);
end_ = 0;
eof_ = false;
std::vector<uint8_t>().swap(buffer_);
// reset callback
if (in(nullptr, 0, SEEK_SET, timeoutMs)) {
return -1;
}
}
inCallback_ = std::forward<DecoderInCallback>(in);
return 1;
}
if (!readBytes(in, maxSeekableBytes + (type ? 8 : 0), timeoutMs)) {
return -1;
}
if (type) {
setImageType(type);
}
if (eof_) {
end_ = 0;
eof_ = false;
// reuse MemoryBuffer functionality
inCallback_ = MemoryBuffer::getCallback(buffer_.data(), buffer_.size());
isSeekable_ = true;
return 1;
}
inCallback_ = std::forward<DecoderInCallback>(in);
return 0;
}
bool SeekableBuffer::readBytes(
DecoderInCallback& in,
size_t maxBytes,
uint64_t timeoutMs) {
// Resize to th minimum 4K page or less
buffer_.resize(std::min(maxBytes, size_t(4 * 1024UL)));
end_ = 0;
eof_ = false;
auto end =
std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMs);
auto watcher = [end]() -> bool {
return std::chrono::steady_clock::now() <= end;
};
bool hasTime = true;
while (!eof_ && end_ < maxBytes && (hasTime = watcher())) {
// lets read all bytes into available buffer
auto res = in(buffer_.data() + end_, buffer_.size() - end_, 0, timeoutMs);
if (res > 0) {
end_ += res;
if (end_ == buffer_.size()) {
buffer_.resize(std::min(size_t(end_ * 4UL), maxBytes));
}
} else if (res == 0) {
eof_ = true;
} else {
// error
return false;
}
}
buffer_.resize(end_);
return hasTime;
}
void SeekableBuffer::setImageType(ImageType* type) {
if (buffer_.size() > 2 && buffer_[0] == 0xFF && buffer_[1] == 0xD8 &&
buffer_[2] == 0xFF) {
*type = ImageType::JPEG;
} else if (
buffer_.size() > 3 && buffer_[1] == 'P' && buffer_[2] == 'N' &&
buffer_[3] == 'G') {
*type = ImageType::PNG;
} else if (
buffer_.size() > 1 &&
((buffer_[0] == 0x49 && buffer_[1] == 0x49) ||
(buffer_[0] == 0x4D && buffer_[1] == 0x4D))) {
*type = ImageType::TIFF;
} else {
*type = ImageType::UNKNOWN;
}
}
int SeekableBuffer::read(uint8_t* buf, int size, uint64_t timeoutMs) {
if (isSeekable_) {
return inCallback_(buf, size, 0, timeoutMs);
}
if (pos_ < end_) {
// read cached bytes for non-seekable callback
auto available = std::min(int(end_ - pos_), size);
memcpy(buf, buffer_.data() + pos_, available);
pos_ += available;
return available;
} else if (!eof_) {
// normal sequential read (see defs.h file), i.e. @buf != null
auto res = inCallback_(buf, size, 0, timeoutMs); // read through
eof_ = res == 0;
return res;
} else {
return 0;
}
}
int64_t SeekableBuffer::seek(int64_t offset, int whence, uint64_t timeoutMs) {
return inCallback_(nullptr, offset, whence, timeoutMs);
}
void SeekableBuffer::shutdown() {
pos_ = end_ = 0;
eof_ = false;
std::vector<uint8_t>().swap(buffer_);
inCallback_ = nullptr;
}
} // namespace ffmpeg
|