Initial pure C++ implementation
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
#include "eac3_transport/eac3_reader.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "foundation/status.h"
|
||||
|
||||
namespace joc::eac3 {
|
||||
|
||||
namespace {
|
||||
constexpr std::size_t kHeaderBytes = 4;
|
||||
} // namespace
|
||||
|
||||
void FrameReader::push(const std::uint8_t* data, std::size_t size) {
|
||||
if (failed_ || data == nullptr || size == 0) {
|
||||
return;
|
||||
}
|
||||
if (consumed_ > 0) {
|
||||
compact();
|
||||
}
|
||||
buffer_.insert(buffer_.end(), data, data + size);
|
||||
}
|
||||
|
||||
void FrameReader::compact() {
|
||||
if (consumed_ == 0) {
|
||||
return;
|
||||
}
|
||||
buffer_.erase(buffer_.begin(), buffer_.begin() + static_cast<std::ptrdiff_t>(consumed_));
|
||||
base_offset_ += consumed_;
|
||||
consumed_ = 0;
|
||||
}
|
||||
|
||||
void FrameReader::fail(joc_error code, std::string message) {
|
||||
failed_ = true;
|
||||
error_ = code;
|
||||
message_ = std::move(message);
|
||||
}
|
||||
|
||||
FrameReader::Next FrameReader::next(Frame* out) {
|
||||
if (failed_) {
|
||||
return Next::Fail;
|
||||
}
|
||||
const std::size_t available = buffer_.size() - consumed_;
|
||||
if (available == 0) {
|
||||
return Next::End;
|
||||
}
|
||||
const std::uint8_t* p = buffer_.data() + consumed_;
|
||||
|
||||
// The reference implementation rejects a frame whose header does not fit,
|
||||
// rather than silently resynchronising on the next 0x0B77.
|
||||
if (available < kHeaderBytes) {
|
||||
if (finished_) {
|
||||
fail(JOC_ERR_EAC3_SYNCFRAME, "E-AC-3 syncframe header truncated at end of input");
|
||||
return Next::Fail;
|
||||
}
|
||||
return Next::End;
|
||||
}
|
||||
|
||||
const std::uint16_t syncword = static_cast<std::uint16_t>((static_cast<std::uint16_t>(p[0]) << 8) | p[1]);
|
||||
if (syncword != kSyncword) {
|
||||
fail(JOC_ERR_EAC3_SYNCFRAME, "invalid E-AC-3 syncword (silent resynchronisation is not allowed)");
|
||||
return Next::Fail;
|
||||
}
|
||||
|
||||
// frmsiz: 11 bits spread over the low 3 bits of byte 2 and all of byte 3,
|
||||
const std::size_t words =
|
||||
static_cast<std::size_t>(((p[2] & 0x07u) << 8) | p[3]) + 1u;
|
||||
const std::size_t frame_bytes = words * 2u;
|
||||
|
||||
if (frame_bytes > available) {
|
||||
if (!finished_) {
|
||||
return Next::End;
|
||||
}
|
||||
fail(JOC_ERR_BITSTREAM_TRUNCATED,
|
||||
"last E-AC-3 syncframe extends past end of input (declared " +
|
||||
std::to_string(frame_bytes) + " bytes, remaining " +
|
||||
std::to_string(available) + ")");
|
||||
return Next::Fail;
|
||||
}
|
||||
|
||||
if (out != nullptr) {
|
||||
out->data = p;
|
||||
out->size = frame_bytes;
|
||||
out->offset = base_offset_ + consumed_;
|
||||
}
|
||||
consumed_ += frame_bytes;
|
||||
stream_offset_ = base_offset_ + consumed_;
|
||||
++frames_emitted_;
|
||||
return Next::Ok;
|
||||
}
|
||||
|
||||
joc_error FrameReader::frame_bytes(const std::uint8_t* data, std::size_t size, std::size_t offset,
|
||||
std::size_t* out_frame_bytes) {
|
||||
if (data == nullptr || out_frame_bytes == nullptr) {
|
||||
return JOC_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if (offset + kHeaderBytes > size) {
|
||||
return JOC_ERR_EAC3_SYNCFRAME;
|
||||
}
|
||||
if (static_cast<std::uint16_t>((static_cast<std::uint16_t>(data[offset]) << 8) | data[offset + 1]) !=
|
||||
kSyncword) {
|
||||
return JOC_ERR_EAC3_SYNCFRAME;
|
||||
}
|
||||
const std::size_t words =
|
||||
static_cast<std::size_t>(((data[offset + 2] & 0x07u) << 8) | data[offset + 3]) + 1u;
|
||||
const std::size_t frame_bytes = words * 2u;
|
||||
if (offset + frame_bytes > size) {
|
||||
return JOC_ERR_BITSTREAM_TRUNCATED;
|
||||
}
|
||||
*out_frame_bytes = frame_bytes;
|
||||
return JOC_OK;
|
||||
}
|
||||
|
||||
} // namespace joc::eac3
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "joc_core.h"
|
||||
|
||||
namespace joc::eac3 {
|
||||
|
||||
struct Frame {
|
||||
const std::uint8_t* data = nullptr;
|
||||
std::size_t size = 0;
|
||||
std::size_t offset = 0; // byte offset of the frame start in the fed stream
|
||||
};
|
||||
|
||||
class FrameReader {
|
||||
public:
|
||||
enum class Next {
|
||||
Ok,
|
||||
End,
|
||||
Fail
|
||||
};
|
||||
|
||||
FrameReader() = default;
|
||||
FrameReader(const std::uint8_t* data, std::size_t size) {
|
||||
push(data, size);
|
||||
finish();
|
||||
}
|
||||
|
||||
// Appends bytes to the internal buffer (used in incremental mode).
|
||||
void push(const std::uint8_t* data, std::size_t size);
|
||||
|
||||
// Declares that no further bytes will arrive; a frame that is still
|
||||
void finish() { finished_ = true; }
|
||||
|
||||
Next next(Frame* out);
|
||||
|
||||
joc_error error() const { return error_; }
|
||||
const std::string& error_message() const { return message_; }
|
||||
|
||||
std::size_t frames_emitted() const { return frames_emitted_; }
|
||||
std::size_t stream_offset() const { return stream_offset_; }
|
||||
|
||||
// report its declared byte length.
|
||||
static joc_error frame_bytes(const std::uint8_t* data, std::size_t size, std::size_t offset,
|
||||
std::size_t* out_frame_bytes);
|
||||
|
||||
static constexpr std::uint16_t kSyncword = 0x0B77;
|
||||
|
||||
private:
|
||||
void compact();
|
||||
void fail(joc_error code, std::string message);
|
||||
|
||||
std::vector<std::uint8_t> buffer_;
|
||||
std::size_t consumed_ = 0; // bytes of buffer_ already turned into frames
|
||||
std::size_t base_offset_ = 0;
|
||||
bool finished_ = false;
|
||||
bool failed_ = false;
|
||||
joc_error error_ = JOC_OK;
|
||||
std::string message_;
|
||||
std::size_t frames_emitted_ = 0;
|
||||
std::size_t stream_offset_ = 0;
|
||||
};
|
||||
|
||||
} // namespace joc::eac3
|
||||
Reference in New Issue
Block a user