Skip to content

Commit

Permalink
Introduced frame headers
Browse files Browse the repository at this point in the history
  • Loading branch information
runei committed Aug 26, 2024
1 parent 83b2bd6 commit 0e43c8a
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 14 deletions.
12 changes: 12 additions & 0 deletions src/internal_modules/roc_audio/iframe_encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ class IFrameEncoder : public core::ArenaAllocation {
//! Get encoded frame size in bytes for given number of samples (per channel).
virtual size_t encoded_byte_count(size_t n_samples) const = 0;

//! Get ponter of headers.
//!
//! @returns
//! A pointer to the headers. May return NULL if not applicable.
virtual const uint8_t* get_headers_frame() const = 0;

//! Get the size of the headers.
//!
//! @returns
//! Size of the headers in bytes.
virtual size_t get_headers_frame_size() const = 0;

//! Start encoding a new frame.
//!
//! @remarks
Expand Down
8 changes: 8 additions & 0 deletions src/internal_modules/roc_audio/pcm_encoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ size_t PcmEncoder::encoded_byte_count(size_t num_samples) const {
return pcm_mapper_.output_byte_count(num_samples * n_chans_);
}

const uint8_t* PcmEncoder::get_headers_frame() const {
return NULL;
}

size_t PcmEncoder::get_headers_frame_size() const {
return 0;
}

void PcmEncoder::begin_frame(void* frame_data, size_t frame_size) {
roc_panic_if_not(frame_data);

Expand Down
6 changes: 6 additions & 0 deletions src/internal_modules/roc_audio/pcm_encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ class PcmEncoder : public IFrameEncoder, public core::NonCopyable<> {
//! Get encoded frame size in bytes for given number of samples per channel.
virtual size_t encoded_byte_count(size_t num_samples) const;

//! Get headers frame.
const uint8_t* get_headers_frame() const;

//! Get the size of the headers.
size_t get_headers_frame_size() const;

//! Start encoding a new frame.
virtual void begin_frame(void* frame, size_t frame_size);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,15 @@ VorbisEncoder::VorbisEncoder(const SampleSpec& sample_spec, core::IArena& arena)
, initialized_(false)
, frame_data_(NULL)
, frame_size_(0)
, current_position_(0) {
vorbis_info_init(&vorbis_info_);

, current_position_(0)
, headers_frame_(NULL)
, headers_frame_size_(0) {
const long num_channels = static_cast<long>(sample_spec.num_channels());
const long sample_rate = static_cast<long>(sample_spec.sample_rate());
const float quality = 0.5f;

if (vorbis_encode_init_vbr(&vorbis_info_, num_channels, sample_rate, quality) != 0) {
roc_panic("vorbis encoder: failed to initialize vorbis encoder");
}

if (vorbis_analysis_init(&vorbis_dsp_, &vorbis_info_) != 0) {
roc_panic("vorbis encoder: failed to initialize vorbis dsp");
}
initialize_structures_(num_channels, sample_rate);

if (vorbis_block_init(&vorbis_dsp_, &vorbis_block_) != 0) {
roc_panic("vorbis encoder: failed to initialize vorbis block");
}
create_headers_frame_();

initialized_ = true;
}
Expand All @@ -44,6 +35,11 @@ VorbisEncoder::~VorbisEncoder() {
vorbis_block_clear(&vorbis_block_);
vorbis_dsp_clear(&vorbis_dsp_);
vorbis_info_clear(&vorbis_info_);
vorbis_comment_clear(&vorbis_comment_);
ogg_stream_clear(&ogg_stream_);
if (headers_frame_) {
free(headers_frame_);
}
}
}

Expand All @@ -64,6 +60,14 @@ size_t VorbisEncoder::encoded_byte_count(size_t n_samples) const {
return total_num_bits / (sample_rate * 8);
}

const uint8_t* VorbisEncoder::get_headers_frame() const {
return headers_frame_;
}

size_t VorbisEncoder::get_headers_frame_size() const {
return headers_frame_size_;
}

void VorbisEncoder::begin_frame(void* frame_data, size_t frame_size) {
roc_panic_if_not(frame_data);

Expand Down Expand Up @@ -91,6 +95,8 @@ size_t VorbisEncoder::write_samples(const sample_t* samples, size_t n_samples) {
}

void VorbisEncoder::end_frame() {
roc_panic_if_not(initialized_);

// Indicate that no more samples are to be written
vorbis_analysis_wrote(&vorbis_dsp_, 0);

Expand All @@ -101,6 +107,98 @@ void VorbisEncoder::end_frame() {
frame_size_ = 0;
}

void VorbisEncoder::initialize_structures_(long num_channels, long sample_rate) {
vorbis_info_init(&vorbis_info_);
vorbis_comment_init(&vorbis_comment_);

const float quality = 0.5f;

// Initialize vorbis_info structure
if (vorbis_encode_init_vbr(&vorbis_info_, num_channels, sample_rate, quality) != 0) {
roc_panic("vorbis encoder: failed to initialize vorbis encoder");
}

// Initialize vorbis_dsp_state for encoding
if (vorbis_analysis_init(&vorbis_dsp_, &vorbis_info_) != 0) {
roc_panic("vorbis encoder: failed to initialize vorbis dsp");
}

// Initialize ogg_stream_state for the stream
if (ogg_stream_init(&ogg_stream_, 0) != 0) {
roc_panic("vorbis encoder: failed to initialize ogg stream");
}

// Initialize vorbis_block for encoding
if (vorbis_block_init(&vorbis_dsp_, &vorbis_block_) != 0) {
roc_panic("vorbis encoder: failed to initialize vorbis block");
}
}

void VorbisEncoder::create_headers_frame_() {
ogg_packet header_packet;
ogg_packet header_comment;
ogg_packet header_codebook;

if (vorbis_analysis_headerout(&vorbis_dsp_, &vorbis_comment_, &header_packet,
&header_comment, &header_codebook)
!= 0) {
roc_panic("vorbis encoder: failed to create vorbis headers");
}

headers_frame_size_ =
calculate_total_headers_size_(header_packet, header_comment, header_codebook);

headers_frame_ = static_cast<uint8_t*>(malloc(headers_frame_size_));
if (!headers_frame_) {
roc_panic("vorbis encoder: failed to allocate memory for headers");
}

copy_headers_to_memory_(header_packet, header_comment, header_codebook);
}

size_t VorbisEncoder::calculate_total_headers_size_(ogg_packet& header_packet,
ogg_packet& header_comment,
ogg_packet& header_codebook) {
ogg_page ogg_page;
long total_size = 0;

insert_headers_into_stream_(header_packet, header_comment, header_codebook);

while (ogg_stream_flush(&ogg_stream_, &ogg_page)) {
total_size += ogg_page.header_len + ogg_page.body_len;
}

return static_cast<size_t>(total_size);
}

void VorbisEncoder::copy_headers_to_memory_(ogg_packet& header_packet,
ogg_packet& header_comment,
ogg_packet& header_codebook) {
ogg_page ogg_page;
size_t offset = 0;

insert_headers_into_stream_(header_packet, header_comment, header_codebook);

while (ogg_stream_flush(&ogg_stream_, &ogg_page)) {
const size_t header_len = static_cast<size_t>(ogg_page.header_len);
const size_t body_len = static_cast<size_t>(ogg_page.body_len);

memcpy(headers_frame_ + offset, ogg_page.header, header_len);
offset += header_len;
memcpy(headers_frame_ + offset, ogg_page.body, body_len);
offset += body_len;
}
}

void VorbisEncoder::insert_headers_into_stream_(ogg_packet& header_packet,
ogg_packet& header_comment,
ogg_packet& header_codebook) {
ogg_stream_reset(&ogg_stream_);
ogg_stream_packetin(&ogg_stream_, &header_packet);
ogg_stream_packetin(&ogg_stream_, &header_comment);
ogg_stream_packetin(&ogg_stream_, &header_codebook);
}

void VorbisEncoder::buffer_samples_(const sample_t* samples, size_t n_samples) {
const int int_n_samples = static_cast<int>(n_samples);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ class VorbisEncoder : public IFrameEncoder {
//! Get encoded frame size in bytes for given number of samples per channel.
virtual size_t encoded_byte_count(size_t n_samples) const;

//! Get combined Vorbis headers.
const uint8_t* get_headers_frame() const;

//! Get the size of the combined headers.
size_t get_headers_frame_size() const;

//! Start encoding a new frame.
virtual void begin_frame(void* frame, size_t frame_size);

Expand All @@ -45,6 +51,17 @@ class VorbisEncoder : public IFrameEncoder {
virtual void end_frame();

private:
void initialize_structures_(long num_channels, long sample_rate);
void create_headers_frame_();
size_t calculate_total_headers_size_(ogg_packet& header_packet,
ogg_packet& header_comment,
ogg_packet& header_codebook);
void copy_headers_to_memory_(ogg_packet& header_packet,
ogg_packet& header_comment,
ogg_packet& header_codebook);
void insert_headers_into_stream_(ogg_packet& header_packet,
ogg_packet& header_comment,
ogg_packet& header_codebook);
void buffer_samples_(const sample_t* samples, size_t n_samples);
void process_encoding_();

Expand All @@ -53,8 +70,13 @@ class VorbisEncoder : public IFrameEncoder {
size_t frame_size_;
size_t current_position_;
vorbis_info vorbis_info_;
vorbis_comment vorbis_comment_;
vorbis_dsp_state vorbis_dsp_;
vorbis_block vorbis_block_;
ogg_stream_state ogg_stream_;

uint8_t* headers_frame_;
size_t headers_frame_size_;
};

} // namespace audio
Expand Down

0 comments on commit 0e43c8a

Please sign in to comment.