int main(int argc, char *argv[]) { /*perform simple round-trip using page reader and writer*/ BitstreamReader *reader = br_open(stdin, BS_LITTLE_ENDIAN); BitstreamWriter *writer = bw_open(stdout, BS_LITTLE_ENDIAN); struct ogg_page page; do { ogg_status result; if ((result = read_ogg_page(reader, &page)) == OGG_OK) { write_ogg_page(writer, &page); } else { fprintf(stderr, "*** Error: %s", ogg_strerror(result)); goto error; } } while (!page.header.stream_end); reader->close(reader); writer->close(writer); return 0; error: reader->close(reader); writer->close(writer); return 1; }
PyObject* encoders_encode_shn(PyObject *dummy, PyObject *args, PyObject *keywds) { static char *kwlist[] = {"filename", "pcmreader", "is_big_endian", "signed_samples", "header_data", "footer_data", "block_size", NULL}; char *filename; FILE *output_file; BitstreamWriter* writer; pcmreader* pcmreader; int is_big_endian = 0; int signed_samples = 0; char* header_data; #ifdef PY_SSIZE_T_CLEAN Py_ssize_t header_size; #else int header_size; #endif char* footer_data = NULL; #ifdef PY_SSIZE_T_CLEAN Py_ssize_t footer_size = 0; #else int footer_size = 0; #endif unsigned block_size = 256; unsigned bytes_written = 0; unsigned i; /*fetch arguments*/ if (!PyArg_ParseTupleAndKeywords(args, keywds, "sO&iis#|s#I", kwlist, &filename, pcmreader_converter, &pcmreader, &is_big_endian, &signed_samples, &header_data, &header_size, &footer_data, &footer_size, &block_size)) return NULL; /*ensure PCMReader is compatible with Shorten*/ if ((pcmreader->bits_per_sample != 8) && (pcmreader->bits_per_sample != 16)) { pcmreader->del(pcmreader); PyErr_SetString(PyExc_ValueError, "unsupported bits per sample"); return NULL; } /*open given filename for writing*/ if ((output_file = fopen(filename, "wb")) == NULL) { PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename); pcmreader->del(pcmreader); return NULL; } else { writer = bw_open(output_file, BS_BIG_ENDIAN); } /*write magic number and version*/ writer->build(writer, "4b 8u", "ajkg", 2); bw_add_callback(writer, byte_counter, &bytes_written); /*write Shorten header*/ write_header(writer, pcmreader->bits_per_sample, is_big_endian, signed_samples, pcmreader->channels, block_size); /*issue initial VERBATIM command with header data*/ write_unsigned(writer, COMMAND_SIZE, FN_VERBATIM); write_unsigned(writer, VERBATIM_SIZE, header_size); for (i = 0; i < header_size; i++) write_unsigned(writer, VERBATIM_BYTE_SIZE, (uint8_t)header_data[i]); /*process PCM frames*/ if (encode_audio(writer, pcmreader, signed_samples, block_size)) goto error; /*if there's footer data, issue a VERBATIM command for it*/ if ((footer_data != NULL) && (footer_size > 0)) { write_unsigned(writer, COMMAND_SIZE, FN_VERBATIM); write_unsigned(writer, VERBATIM_SIZE, footer_size); for (i = 0; i < footer_size; i++) write_unsigned(writer, VERBATIM_BYTE_SIZE, (uint8_t)footer_data[i]); } /*issue QUIT command*/ write_unsigned(writer, COMMAND_SIZE, FN_QUIT); /*pad output (not including header) to a multiple of 4 bytes*/ writer->byte_align(writer); while ((bytes_written % 4) != 0) { writer->write(writer, 8, 0); } /*deallocate temporary buffers and close files*/ pcmreader->del(pcmreader); writer->close(writer); Py_INCREF(Py_None); return Py_None; error: pcmreader->del(pcmreader); writer->close(writer); return NULL; }
static result_t encode_opus_file(char *filename, pcmreader *pcmreader, int quality, unsigned original_sample_rate) { const int multichannel = (pcmreader->channels > 2); const unsigned channel_mapping = (pcmreader->channels > 8 ? 255 : pcmreader->channels > 2); int stream_count; int coupled_stream_count; unsigned char stream_map[255]; result_t result = ENCODE_OK; FILE *output_file = NULL; ogg_stream_state ogg_stream; ogg_page ogg_page; OpusEncoder *opus_encoder = NULL; OpusMSEncoder *opus_ms_encoder = NULL; int error; aa_int *samples = NULL; opus_int16 *opus_samples = NULL; unsigned char opus_frame[OPUS_FRAME_LEN]; ogg_int64_t granulepos = 0; ogg_int64_t packetno = 0; opus_int32 preskip; /*open output file for writing*/ if ((output_file = fopen(filename, "w+b")) == NULL) { return ERR_IOERROR; } if (!multichannel) { if ((opus_encoder = opus_encoder_create(48000, pcmreader->channels, OPUS_APPLICATION_AUDIO, &error)) == NULL) { fclose(output_file); return ERR_ENCODER_INIT; } opus_encoder_ctl(opus_encoder, OPUS_SET_COMPLEXITY(quality)); opus_encoder_ctl(opus_encoder, OPUS_GET_LOOKAHEAD(&preskip)); } else { if ((opus_ms_encoder = opus_multistream_surround_encoder_create( 48000, pcmreader->channels, channel_mapping, &stream_count, &coupled_stream_count, stream_map, OPUS_APPLICATION_AUDIO, &error)) == NULL) { fclose(output_file); return ERR_ENCODER_INIT; } opus_multistream_encoder_ctl(opus_ms_encoder, OPUS_SET_COMPLEXITY(quality)); opus_multistream_encoder_ctl(opus_ms_encoder, OPUS_GET_LOOKAHEAD(&preskip)); } srand((unsigned)time(NULL)); ogg_stream_init(&ogg_stream, rand()); /*write header and comment packets to Ogg stream*/ { BitstreamWriter *header = bw_open_recorder(BS_LITTLE_ENDIAN); BitstreamWriter *comment = bw_open_recorder(BS_LITTLE_ENDIAN); int i; /*write header packet to Ogg stream*/ const char opushead[] = "OpusHead"; const char opuscomment[] = "OpusTags"; const char *vendor_string = opus_get_version_string(); const size_t vendor_string_len = strlen(vendor_string); ogg_packet packet_head; ogg_packet packet_tags; header->write_bytes(header, (uint8_t*)opushead, (unsigned)strlen(opushead)); header->write(header, 8, 1); /*version*/ header->write(header, 8, pcmreader->channels); header->write(header, 16, preskip); header->write(header, 32, original_sample_rate); header->write(header, 16, 0); /*output gain*/ header->write(header, 8, channel_mapping); if (channel_mapping != 0) { header->write(header, 8, stream_count); header->write(header, 8, coupled_stream_count); for (i = 0; i < pcmreader->channels; i++) { header->write(header, 8, stream_map[i]); } } packet_head.packet = BUF_WINDOW_START(header->output.buffer); packet_head.bytes = BUF_WINDOW_SIZE(header->output.buffer); packet_head.b_o_s = 1; packet_head.e_o_s = 0; packet_head.granulepos = 0; packet_head.packetno = packetno++; ogg_stream_packetin(&ogg_stream, &packet_head); for (i = ogg_stream_flush(&ogg_stream, &ogg_page); i != 0; i = ogg_stream_flush(&ogg_stream, &ogg_page)) { fwrite(ogg_page.header, 1, ogg_page.header_len, output_file); fwrite(ogg_page.body, 1, ogg_page.body_len, output_file); } /*write comment packet to Ogg stream*/ comment->write_bytes(comment, (uint8_t*)opuscomment, (unsigned)strlen(opuscomment)); comment->write(comment, 32, (unsigned)vendor_string_len); comment->write_bytes(comment, (uint8_t*)vendor_string, (unsigned)vendor_string_len); comment->write(comment, 32, 0); packet_tags.packet = BUF_WINDOW_START(comment->output.buffer); packet_tags.bytes = BUF_WINDOW_SIZE(comment->output.buffer); packet_tags.b_o_s = 0; packet_tags.e_o_s = 0; packet_tags.granulepos = 0; packet_tags.packetno = packetno++; ogg_stream_packetin(&ogg_stream, &packet_tags); for (i = ogg_stream_flush(&ogg_stream, &ogg_page); i != 0; i = ogg_stream_flush(&ogg_stream, &ogg_page)) { fwrite(ogg_page.header, 1, ogg_page.header_len, output_file); fwrite(ogg_page.body, 1, ogg_page.body_len, output_file); } header->close(header); comment->close(comment); } samples = aa_int_new(); opus_samples = malloc(sizeof(opus_int16) * pcmreader->channels * BLOCK_SIZE); /*for each non-empty FrameList from PCMReader, encode Opus frame*/ if (pcmreader->read(pcmreader, BLOCK_SIZE, samples)) { result = ERR_PCMREADER; goto cleanup; } else if (samples->_[0]->len > BLOCK_SIZE) { result = ERR_BLOCK_SIZE; goto cleanup; } while (samples->_[0]->len > 0) { const int short_framelist = (samples->_[0]->len < BLOCK_SIZE); unsigned c; opus_int32 encoded_size; ogg_packet packet; granulepos += samples->_[0]->len; /*pad FrameList with additional null samples if necessary*/ for (c = 0; c < samples->len; c++) { a_int *channel = samples->_[c]; channel->mappend(channel, BLOCK_SIZE - samples->_[0]->len, 0); } /*rearrange channels to Vorbis order if necessary*/ reorder_channels(pcmreader->channel_mask, samples); /*place samples in interleaved buffer*/ for (c = 0; c < samples->len; c++) { unsigned i; a_int *channel = samples->_[c]; for (i = 0; i < channel->len; i++) { opus_samples[c + (i * samples->len)] = (opus_int16)channel->_[i]; } } /*call opus_encode on interleaved buffer to get next packet*/ if (!multichannel) { encoded_size = opus_encode(opus_encoder, opus_samples, samples->_[0]->len, opus_frame, OPUS_FRAME_LEN); } else { encoded_size = opus_multistream_encode(opus_ms_encoder, opus_samples, samples->_[0]->len, opus_frame, OPUS_FRAME_LEN); } /*get next FrameList to encode*/ if (pcmreader->read(pcmreader, BLOCK_SIZE, samples)) { result = ERR_PCMREADER; goto cleanup; } else if (samples->_[0]->len > BLOCK_SIZE) { result = ERR_BLOCK_SIZE; goto cleanup; } /*dump Opus packet to Ogg stream*/ /*do this *after* reading the next FrameList in order to detect the end of stream properly based on whether the FrameList has no frames*/ packet.packet = (unsigned char *)opus_frame; packet.bytes = encoded_size; packet.b_o_s = 0; packet.e_o_s = (short_framelist || (samples->_[0]->len == 0)); packet.granulepos = granulepos; packet.packetno = packetno; ogg_stream_packetin(&ogg_stream, &packet); while (ogg_stream_pageout(&ogg_stream, &ogg_page)) { fwrite(ogg_page.header, 1, ogg_page.header_len, output_file); fwrite(ogg_page.body, 1, ogg_page.body_len, output_file); } } /*flush any remaining Ogg pages to disk*/ while (ogg_stream_flush(&ogg_stream, &ogg_page)) { fwrite(ogg_page.header, 1, ogg_page.header_len, output_file); fwrite(ogg_page.body, 1, ogg_page.body_len, output_file); } cleanup: fclose(output_file); ogg_stream_clear(&ogg_stream); if (!multichannel) { opus_encoder_destroy(opus_encoder); } else { opus_multistream_encoder_destroy(opus_ms_encoder); } samples->del(samples); free(opus_samples); return result; }