seekable_buffer.cpp 3.49 KB
Newer Older
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
140
141
142
143
144
145
146
147
148
// Copyright 2004-present Facebook. All Rights Reserved.

#include "seekable_buffer.h"
#include <c10/util/Logging.h>
#include <chrono>

extern "C" {
#include <libavformat/avio.h>
}

namespace ffmpeg {

bool SeekableBuffer::init(
    DecoderInCallback&& in,
    ssize_t minSize,
    ssize_t maxSize,
    uint64_t timeoutMs) {
  inCallback_ = std::forward<DecoderInCallback>(in);
  len_ = minSize;
  buffer_.resize(len_);
  pos_ = 0;
  end_ = 0;
  eof_ = 0;

  auto end =
      std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMs);
  auto watcher = [end]() -> bool {
    return std::chrono::steady_clock::now() <= end;
  };

  bool hasTime = false;
  while (!eof_ && end_ < maxSize && (hasTime = watcher())) {
    // lets read all bytes into available buffer
    auto res = inCallback_(buffer_.data() + end_, len_ - end_, timeoutMs);
    if (res > 0) {
      end_ += res;
      if (end_ == len_) {
        len_ = std::min(len_ * 4, maxSize);
        buffer_.resize(len_);
      }
    } else if (res == 0) {
      eof_ = 1;
    } else {
      // error
      return false;
    }
  }

  if (!hasTime) {
    return false;
  }

  if (buffer_.size() > 2 && buffer_[0] == 0xFF && buffer_[1] == 0xD8 &&
      buffer_[2] == 0xFF) {
    imageType_ = ImageType::JPEG;
  } else if (
      buffer_.size() > 3 && buffer_[1] == 'P' && buffer_[2] == 'N' &&
      buffer_[3] == 'G') {
    imageType_ = ImageType::PNG;
  } else if (
      buffer_.size() > 1 &&
      ((buffer_[0] == 0x49 && buffer_[1] == 0x49) ||
       (buffer_[0] == 0x4D && buffer_[1] == 0x4D))) {
    imageType_ = ImageType::TIFF;
  }

  return true;
}

int SeekableBuffer::read(uint8_t* buf, int size, uint64_t timeoutMs) {
  // 1. pos_ < end_
  if (pos_ < end_) {
    auto available = std::min(int(end_ - pos_), size);
    memcpy(buf, buffer_.data() + pos_, available);
    pos_ += available;
    return available;
  } else if (!eof_) {
    auto res = inCallback_(buf, size, timeoutMs); // read through
    if (res > 0) {
      pos_ += res;
      if (pos_ > end_ && !buffer_.empty()) {
        std::vector<uint8_t>().swap(buffer_);
      }
    } else if (res == 0) {
      eof_ = 1;
    }
    return res;
  } else {
    return 0;
  }
}

int64_t SeekableBuffer::seek(int64_t offset, int whence, uint64_t timeoutMs) {
  // remove force flag
  whence &= ~AVSEEK_FORCE;
  // get size request
  int size = whence & AVSEEK_SIZE;
  // remove size flag
  whence &= ~AVSEEK_SIZE;

  if (size) {
    return eof_ ? end_ : AVERROR(EINVAL);
  } else {
    switch (whence) {
      case SEEK_SET:
        if (offset < 0) {
          return AVERROR(EINVAL);
        }
        if (offset <= end_) {
          pos_ = offset;
          return pos_;
        }
        if (!inCallback_(0, offset, timeoutMs)) {
          pos_ = offset;
          return 0;
        }
        break;
      case SEEK_END:
        if (eof_ && pos_ <= end_ && offset < 0 && end_ + offset >= 0) {
          pos_ = end_ + offset;
          return 0;
        }
        break;
      case SEEK_CUR:
        if (pos_ + offset < 0) {
          return AVERROR(EINVAL);
        }
        if (pos_ + offset <= end_) {
          pos_ += offset;
          return 0;
        }
        if (!inCallback_(0, pos_ + offset, timeoutMs)) {
          pos_ += offset;
          return 0;
        }
        break;
      default:
        LOG(ERROR) << "Unknown whence flag gets provided: " << whence;
    }
  }
  return AVERROR(EINVAL); // we have no idea what the media size is
}

void SeekableBuffer::shutdown() {
  inCallback_ = nullptr;
}

} // namespace ffmpeg