content-streamer.cc 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
  2. #include "content-streamer.h"
  3. #include "led-matrix.h"
  4. #include <fcntl.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <sys/stat.h>
  8. #include <sys/types.h>
  9. #include <unistd.h>
  10. #include <algorithm>
  11. #include "gpio-bits.h"
  12. namespace rgb_matrix {
  13. // Pre-c++11 helper
  14. #define STATIC_ASSERT(msg, c) typedef int static_assert_##msg[(c) ? 1 : -1]
  15. namespace {
  16. // We write magic values as integers to automatically detect endian issues.
  17. // Streams are stored in little-endian. This is the ARM default (running
  18. // the Raspberry Pi, but also x86; so it is possible to create streams easily
  19. // on a different x86 Linux PC.
  20. static const uint32_t kFileMagicValue = 0xED0C5A48;
  21. struct FileHeader {
  22. uint32_t magic; // kFileMagicValue
  23. uint32_t buf_size;
  24. uint32_t width;
  25. uint32_t height;
  26. uint64_t future_use1;
  27. uint64_t is_wide_gpio : 1;
  28. uint64_t flags_future_use : 63;
  29. };
  30. STATIC_ASSERT(file_header_size_changed, sizeof(FileHeader) == 32);
  31. static const uint32_t kFrameMagicValue = 0x12345678;
  32. struct FrameHeader {
  33. uint32_t magic; // kFrameMagic
  34. uint32_t size;
  35. uint32_t hold_time_us; // How long this frame lasts in usec.
  36. uint32_t future_use1;
  37. uint64_t future_use2;
  38. uint64_t future_use3;
  39. };
  40. STATIC_ASSERT(file_header_size_changed, sizeof(FrameHeader) == 32);
  41. }
  42. FileStreamIO::FileStreamIO(int fd) : fd_(fd) {
  43. posix_fadvise(fd_, 0, 0, POSIX_FADV_SEQUENTIAL);
  44. }
  45. FileStreamIO::~FileStreamIO() { close(fd_); }
  46. void FileStreamIO::Rewind() { lseek(fd_, 0, SEEK_SET); }
  47. ssize_t FileStreamIO::Read(void *buf, const size_t count) {
  48. return read(fd_, buf, count);
  49. }
  50. ssize_t FileStreamIO::Append(const void *buf, const size_t count) {
  51. return write(fd_, buf, count);
  52. }
  53. void MemStreamIO::Rewind() { pos_ = 0; }
  54. ssize_t MemStreamIO::Read(void *buf, size_t count) {
  55. const size_t amount = std::min(count, buffer_.size() - pos_);
  56. memcpy(buf, buffer_.data() + pos_, amount);
  57. pos_ += amount;
  58. return amount;
  59. }
  60. ssize_t MemStreamIO::Append(const void *buf, size_t count) {
  61. buffer_.append((const char*)buf, count);
  62. return count;
  63. }
  64. // Read exactly count bytes including retries. Returns success.
  65. static bool FullRead(StreamIO *io, void *buf, const size_t count) {
  66. int remaining = count;
  67. char *char_buffer = (char*)buf;
  68. while (remaining > 0) {
  69. int r = io->Read(char_buffer, remaining);
  70. if (r < 0) return false;
  71. if (r == 0) break; // EOF.
  72. char_buffer += r; remaining -= r;
  73. }
  74. return remaining == 0;
  75. }
  76. // Write exactly count bytes including retries. Returns success.
  77. static bool FullAppend(StreamIO *io, const void *buf, const size_t count) {
  78. int remaining = count;
  79. const char *char_buffer = (const char*) buf;
  80. while (remaining > 0) {
  81. int w = io->Append(char_buffer, remaining);
  82. if (w < 0) return false;
  83. char_buffer += w; remaining -= w;
  84. }
  85. return remaining == 0;
  86. }
  87. StreamWriter::StreamWriter(StreamIO *io) : io_(io), header_written_(false) {}
  88. bool StreamWriter::Stream(const FrameCanvas &frame, uint32_t hold_time_us) {
  89. const char *data;
  90. size_t len;
  91. frame.Serialize(&data, &len);
  92. if (!header_written_) {
  93. WriteFileHeader(frame, len);
  94. }
  95. FrameHeader h = {};
  96. h.magic = kFrameMagicValue;
  97. h.size = len;
  98. h.hold_time_us = hold_time_us;
  99. FullAppend(io_, &h, sizeof(h));
  100. return FullAppend(io_, data, len) == (ssize_t)len;
  101. }
  102. void StreamWriter::WriteFileHeader(const FrameCanvas &frame, size_t len) {
  103. FileHeader header = {};
  104. header.magic = kFileMagicValue;
  105. header.width = frame.width();
  106. header.height = frame.height();
  107. header.buf_size = len;
  108. header.is_wide_gpio = (sizeof(gpio_bits_t) > 4);
  109. FullAppend(io_, &header, sizeof(header));
  110. header_written_ = true;
  111. }
  112. StreamReader::StreamReader(StreamIO *io)
  113. : io_(io), state_(STREAM_AT_BEGIN), header_frame_buffer_(NULL) {
  114. io_->Rewind();
  115. }
  116. StreamReader::~StreamReader() { delete [] header_frame_buffer_; }
  117. void StreamReader::Rewind() {
  118. io_->Rewind();
  119. state_ = STREAM_AT_BEGIN;
  120. }
  121. bool StreamReader::GetNext(FrameCanvas *frame, uint32_t* hold_time_us) {
  122. if (state_ == STREAM_AT_BEGIN && !ReadFileHeader(*frame)) return false;
  123. if (state_ != STREAM_READING) return false;
  124. // Read header and expected buffer size.
  125. if (!FullRead(io_, header_frame_buffer_,
  126. sizeof(FrameHeader) + frame_buf_size_)) {
  127. return false;
  128. }
  129. const FrameHeader &h = *reinterpret_cast<FrameHeader*>(header_frame_buffer_);
  130. // TODO: we might allow for this to be a kFileMagicValue, to allow people
  131. // to just concatenate streams. In that case, we just would need to read
  132. // ahead past this header (both headers are designed to be same size)
  133. if (h.magic != kFrameMagicValue) {
  134. state_ = STREAM_ERROR;
  135. return false;
  136. }
  137. // In the future, we might allow larger buffers (audio?), but never smaller.
  138. // For now, we need to make sure to exactly match the size, as our assumption
  139. // above is that we can read the full header + frame in one FullRead().
  140. if (h.size != frame_buf_size_)
  141. return false;
  142. if (hold_time_us) *hold_time_us = h.hold_time_us;
  143. return frame->Deserialize(header_frame_buffer_ + sizeof(FrameHeader),
  144. frame_buf_size_);
  145. }
  146. bool StreamReader::ReadFileHeader(const FrameCanvas &frame) {
  147. FileHeader header;
  148. FullRead(io_, &header, sizeof(header));
  149. if (header.magic != kFileMagicValue) {
  150. state_ = STREAM_ERROR;
  151. return false;
  152. }
  153. if ((int)header.width != frame.width()
  154. || (int)header.height != frame.height()) {
  155. fprintf(stderr, "This stream is for %dx%d, can't play on %dx%d. "
  156. "Please use the same settings for record/replay\n",
  157. header.width, header.height, frame.width(), frame.height());
  158. state_ = STREAM_ERROR;
  159. return false;
  160. }
  161. if (header.is_wide_gpio != (sizeof(gpio_bits_t) == 8)) {
  162. fprintf(stderr, "This stream was written with %s GPIO width support but "
  163. "this library is compiled with %d bit GPIO width (see "
  164. "ENABLE_WIDE_GPIO_COMPUTE_MODULE setting in lib/Makefile)\n",
  165. header.is_wide_gpio ? "wide (64-bit)" : "narrow (32-bit)",
  166. int(sizeof(gpio_bits_t) * 8));
  167. state_ = STREAM_ERROR;
  168. return false;
  169. }
  170. state_ = STREAM_READING;
  171. frame_buf_size_ = header.buf_size;
  172. if (!header_frame_buffer_)
  173. header_frame_buffer_ = new char [ sizeof(FrameHeader) + header.buf_size ];
  174. return true;
  175. }
  176. } // namespace rgb_matrix