static result_t encode_opus_file(char *filename, struct 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]; int samples[BLOCK_SIZE * pcmreader->channels]; unsigned pcm_frames; 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; 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*/ { BitstreamRecorder *header = bw_open_bytes_recorder(BS_LITTLE_ENDIAN); BitstreamWriter *header_w =(BitstreamWriter*)header; BitstreamRecorder *comment = bw_open_bytes_recorder(BS_LITTLE_ENDIAN); BitstreamWriter *comment_w = (BitstreamWriter*)comment; 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_w->write_bytes(header_w, (uint8_t*)opushead, (unsigned)strlen(opushead)); header_w->write(header_w, 8, 1); /*version*/ header_w->write(header_w, 8, pcmreader->channels); header_w->write(header_w, 16, preskip); header_w->write(header_w, 32, original_sample_rate); header_w->write(header_w, 16, 0); /*output gain*/ header_w->write(header_w, 8, channel_mapping); if (channel_mapping != 0) { header_w->write(header_w, 8, stream_count); header_w->write(header_w, 8, coupled_stream_count); for (i = 0; i < pcmreader->channels; i++) { header_w->write(header_w, 8, stream_map[i]); } } packet_head.packet = malloc(header->bytes_written(header)); header->data(header, (uint8_t*)packet_head.packet); packet_head.bytes = header->bytes_written(header); packet_head.b_o_s = 1; packet_head.e_o_s = 0; packet_head.granulepos = 0; packet_head.packetno = packetno++; header->close(header); 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); } free(packet_head.packet); /*write comment packet to Ogg stream*/ comment_w->write_bytes(comment_w, (uint8_t*)opuscomment, (unsigned)strlen(opuscomment)); comment_w->write(comment_w, 32, (unsigned)vendor_string_len); comment_w->write_bytes(comment_w, (uint8_t*)vendor_string, (unsigned)vendor_string_len); comment_w->write(comment_w, 32, 0); packet_tags.packet = malloc(comment->bytes_written(comment)); comment->data(comment, (uint8_t*)packet_tags.packet); packet_tags.bytes = comment->bytes_written(comment); packet_tags.b_o_s = 0; packet_tags.e_o_s = 0; packet_tags.granulepos = 0; packet_tags.packetno = packetno++; comment->close(comment); 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); } free(packet_tags.packet); } opus_samples = malloc(sizeof(opus_int16) * pcmreader->channels * BLOCK_SIZE); pcm_frames = pcmreader->read(pcmreader, BLOCK_SIZE, samples); if (!pcm_frames && (pcmreader->status != PCM_OK)) { result = ERR_PCMREADER; goto cleanup; } /*for each non-empty FrameList from PCMReader, encode Opus frame*/ while (pcm_frames) { const int short_framelist = (pcm_frames < BLOCK_SIZE); unsigned i; opus_int32 encoded_size; ogg_packet packet; granulepos += pcm_frames; /*pad FrameList with additional null samples if necessary*/ memset(samples + pcm_frames * pcmreader->channels, 0, sizeof(int) * (BLOCK_SIZE - pcm_frames) * pcmreader->channels); /*rearrange channels to Vorbis order if necessary*/ reorder_channels(pcmreader->channel_mask, BLOCK_SIZE, samples); /*place samples in interleaved buffer*/ for (i = 0; i < (pcm_frames * pcmreader->channels); i++) { opus_samples[i] = (opus_int16)samples[i]; } /*call opus_encode on interleaved buffer to get next packet*/ if (!multichannel) { encoded_size = opus_encode(opus_encoder, opus_samples, BLOCK_SIZE, opus_frame, OPUS_FRAME_LEN); } else { encoded_size = opus_multistream_encode(opus_ms_encoder, opus_samples, BLOCK_SIZE, opus_frame, OPUS_FRAME_LEN); } /*get next FrameList to encode*/ pcm_frames = pcmreader->read(pcmreader, BLOCK_SIZE, samples); if (!pcm_frames && (pcmreader->status != PCM_OK)) { result = ERR_PCMREADER; 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 || (pcm_frames == 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); } free(opus_samples); return result; }
static int OpenEncoder(vlc_object_t *p_this) { encoder_t *enc = (encoder_t *)p_this; if (enc->fmt_out.i_codec != VLC_CODEC_OPUS) return VLC_EGENERIC; encoder_sys_t *sys = malloc(sizeof(*sys)); if (!sys) return VLC_ENOMEM; int status = VLC_SUCCESS; sys->buffer = NULL; sys->enc = NULL; enc->pf_encode_audio = Encode; enc->fmt_in.i_codec = VLC_CODEC_FL32; enc->fmt_in.audio.i_rate = /* Only 48kHz */ enc->fmt_out.audio.i_rate = 48000; enc->fmt_out.audio.i_channels = enc->fmt_in.audio.i_channels; OpusHeader header; if (opus_prepare_header(enc->fmt_out.audio.i_channels, enc->fmt_out.audio.i_rate, &header)) { msg_Err(enc, "Failed to prepare header."); status = VLC_ENOMEM; goto error; } /* needed for max encoded size calculation */ sys->nb_streams = header.nb_streams; int err; sys->enc = opus_multistream_surround_encoder_create(enc->fmt_in.audio.i_rate, enc->fmt_in.audio.i_channels, header.channel_mapping, &header.nb_streams, &header.nb_coupled, header.stream_map, OPUS_APPLICATION_AUDIO, &err); if (err != OPUS_OK) { msg_Err(enc, "Could not create encoder: error %d", err); sys->enc = NULL; status = VLC_EGENERIC; goto error; } /* TODO: vbr, bitrate, fec */ /* Buffer for incoming audio, since opus only accepts frame sizes that are multiples of 2.5ms */ enc->p_sys = sys; sys->buffer = malloc(OPUS_FRAME_SIZE * header.channels * sizeof(float)); if (!sys->buffer) { status = VLC_ENOMEM; goto error; } sys->i_nb_samples = 0; sys->i_samples_delay = 0; int ret = opus_multistream_encoder_ctl(enc->p_sys->enc, OPUS_GET_LOOKAHEAD(&sys->i_samples_delay)); if (ret != OPUS_OK) msg_Err(enc, "Unable to get number of lookahead samples: %s\n", opus_strerror(ret)); header.preskip = sys->i_samples_delay; /* Now that we have preskip, we can write the header to extradata */ if (opus_write_header((uint8_t **) &enc->fmt_out.p_extra, &enc->fmt_out.i_extra, &header)) { msg_Err(enc, "Failed to write header."); status = VLC_ENOMEM; goto error; } if (sys->i_samples_delay > 0) { const unsigned padding_samples = sys->i_samples_delay * enc->fmt_out.audio.i_channels; sys->padding = block_Alloc(padding_samples * sizeof(float)); if (!sys->padding) { status = VLC_ENOMEM; goto error; } sys->padding->i_nb_samples = sys->i_samples_delay; float *pad_ptr = (float *) sys->padding->p_buffer; memset(pad_ptr, 0, padding_samples * sizeof(float)); } else { sys->padding = NULL; } return status; error: if (sys->enc) opus_multistream_encoder_destroy(sys->enc); free(sys->buffer); free(sys); return status; }
virtual bool CookSurround(FName Format, const TArray<TArray<uint8> >& SrcBuffers, FSoundQualityInfo& QualityInfo, TArray<uint8>& CompressedDataStore) const override { check(Format == NAME_OPUS); // Get best compatible sample rate const uint16 kOpusSampleRate = GetBestOutputSampleRate(QualityInfo.SampleRate); // Frame size must be one of 2.5, 5, 10, 20, 40 or 60 ms const int32 kOpusFrameSizeMs = 60; // Calculate frame size required by Opus const int32 kOpusFrameSizeSamples = (kOpusSampleRate * kOpusFrameSizeMs) / 1000; const uint32 kSampleStride = SAMPLE_SIZE * QualityInfo.NumChannels; const int32 kBytesPerFrame = kOpusFrameSizeSamples * kSampleStride; // Check whether source has compatible sample rate TArray<TArray<uint8>> SrcBufferCopies; if (QualityInfo.SampleRate != kOpusSampleRate) { for (int32 Index = 0; Index < SrcBuffers.Num(); Index++) { TArray<uint8>& NewCopy = *new (SrcBufferCopies) TArray<uint8>; if (!ResamplePCM(1, SrcBuffers[Index], QualityInfo.SampleRate, NewCopy, kOpusSampleRate)) { return false; } } } else { // Take a copy of the source regardless for (int32 Index = 0; Index < SrcBuffers.Num(); Index++) { SrcBufferCopies[Index] = SrcBuffers[Index]; } } // Ensure that all channels are the same length int32 SourceSize = -1; for (int32 Index = 0; Index < SrcBufferCopies.Num(); Index++) { if (!Index) { SourceSize = SrcBufferCopies[Index].Num(); } else { if (SourceSize != SrcBufferCopies[Index].Num()) { return false; } } } if (SourceSize <= 0) { return false; } // Initialise the Opus multistream encoder OpusMSEncoder* Encoder = NULL; int32 EncError = 0; int32 streams = 0; int32 coupled_streams = 0; // mapping_family not documented but figured out: 0 = 1 or 2 channels, 1 = 1 to 8 channel surround sound, 255 = up to 255 channels with no surround processing int32 mapping_family = 1; TArray<uint8> mapping; mapping.AddUninitialized(QualityInfo.NumChannels); #if USE_UE4_MEM_ALLOC int32 EncSize = opus_multistream_surround_encoder_get_size(QualityInfo.NumChannels, mapping_family); Encoder = (OpusMSEncoder*)FMemory::Malloc(EncSize); EncError = opus_multistream_surround_encoder_init(Encoder, kOpusSampleRate, QualityInfo.NumChannels, mapping_family, &streams, &coupled_streams, mapping.GetData(), OPUS_APPLICATION_AUDIO); #else Encoder = opus_multistream_surround_encoder_create(kOpusSampleRate, QualityInfo.NumChannels, mapping_family, &streams, &coupled_streams, mapping.GetData(), OPUS_APPLICATION_AUDIO, &EncError); #endif if (EncError != OPUS_OK) { Destroy(Encoder); return false; } int32 BitRate = GetBitRateFromQuality(QualityInfo); opus_multistream_encoder_ctl(Encoder, OPUS_SET_BITRATE(BitRate)); // Create a buffer to store compressed data CompressedDataStore.Empty(); FMemoryWriter CompressedData(CompressedDataStore); int32 SrcBufferOffset = 0; // Calc frame and sample count int32 FramesToEncode = SourceSize / (kOpusFrameSizeSamples * SAMPLE_SIZE); uint32 TrueSampleCount = SourceSize / SAMPLE_SIZE; // Add another frame if Source does not divide into an equal number of frames if (SourceSize % (kOpusFrameSizeSamples * SAMPLE_SIZE) != 0) { FramesToEncode++; } check(QualityInfo.NumChannels <= MAX_uint8); check(FramesToEncode <= MAX_uint16); SerializeHeaderData(CompressedData, kOpusSampleRate, TrueSampleCount, QualityInfo.NumChannels, FramesToEncode); // Temporary storage for source data in an interleaved format TArray<uint8> TempInterleavedSrc; TempInterleavedSrc.AddUninitialized(kBytesPerFrame); // Temporary storage with more than enough to store any compressed frame TArray<uint8> TempCompressedData; TempCompressedData.AddUninitialized(kBytesPerFrame); while (SrcBufferOffset < SourceSize) { // Read a frames worth of data from the source and pack it into interleaved temporary storage for (int32 SampleIndex = 0; SampleIndex < kOpusFrameSizeSamples; ++SampleIndex) { int32 CurrSrcOffset = SrcBufferOffset + SampleIndex*SAMPLE_SIZE; int32 CurrInterleavedOffset = SampleIndex*kSampleStride; if (CurrSrcOffset < SourceSize) { for (uint32 ChannelIndex = 0; ChannelIndex < QualityInfo.NumChannels; ++ChannelIndex) { // Interleave the channels in the Vorbis format, so that the correct channel is used for LFE int32 OrderedChannelIndex = VorbisChannelInfo::Order[QualityInfo.NumChannels - 1][ChannelIndex]; int32 CurrInterleavedIndex = CurrInterleavedOffset + ChannelIndex*SAMPLE_SIZE; // Copy both bytes that make up a single sample TempInterleavedSrc[CurrInterleavedIndex] = SrcBufferCopies[OrderedChannelIndex][CurrSrcOffset]; TempInterleavedSrc[CurrInterleavedIndex + 1] = SrcBufferCopies[OrderedChannelIndex][CurrSrcOffset + 1]; } } else { // Zero the rest of the temp buffer to make it an exact frame FMemory::Memzero(TempInterleavedSrc.GetData() + CurrInterleavedOffset, kBytesPerFrame - CurrInterleavedOffset); SampleIndex = kOpusFrameSizeSamples; } } int32 CompressedLength = opus_multistream_encode(Encoder, (const opus_int16*)(TempInterleavedSrc.GetData()), kOpusFrameSizeSamples, TempCompressedData.GetData(), TempCompressedData.Num()); if (CompressedLength < 0) { const char* ErrorStr = opus_strerror(CompressedLength); UE_LOG(LogAudio, Warning, TEXT("Failed to encode: [%d] %s"), CompressedLength, ANSI_TO_TCHAR(ErrorStr)); Destroy(Encoder); CompressedDataStore.Empty(); return false; } else { // Store frame length and copy compressed data before incrementing pointers check(CompressedLength < MAX_uint16); SerialiseFrameData(CompressedData, TempCompressedData.GetData(), CompressedLength); SrcBufferOffset += kOpusFrameSizeSamples * SAMPLE_SIZE; } } Destroy(Encoder); return CompressedDataStore.Num() > 0; }
/* * Create opus writer port. */ PJ_DEF(pj_status_t) pjmedia_opus_writer_port_create( pj_pool_t *pool, const char *filename, unsigned sampling_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned flags, pj_ssize_t buff_size, pjmedia_port **p_port ) { struct opus_port *oport; struct opus_tool_param *otp; pj_str_t name; pj_status_t status; pjmedia_frame tempframe; int i,ret; /* Check arguments. */ PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL); /* Only supports 16bits per sample for now. */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); /* Create file port instance. */ oport = PJ_POOL_ZALLOC_T(pool, struct opus_port); PJ_ASSERT_RETURN(oport != NULL, PJ_ENOMEM); /* Initialize port info. */ pj_strdup2(pool, &name, filename); pjmedia_port_info_init(&oport->base.info, &name, SIGNATURE, sampling_rate, channel_count, bits_per_sample, samples_per_frame); oport->base.get_frame = &opus_get_frame; oport->base.put_frame = &opus_put_frame; oport->base.on_destroy = &opus_on_destroy; oport->bytes_per_sample = 2; /********************** * opus-tool Start *********************/ otp = PJ_POOL_ZALLOC_T(pool, struct opus_tool_param); PJ_ASSERT_RETURN(otp != NULL, PJ_ENOMEM); /* Initialize some default parameters. */ otp->opt_ctls_ctlval=NULL; otp->frange=NULL; otp->range_file=NULL; otp->inopt.channels=channel_count; otp->inopt.rate=otp->coding_rate=otp->rate=sampling_rate; /* Some default value */ otp->frame_size=960; otp->bitrate=-1; otp->with_hard_cbr=0; otp->with_cvbr=0; otp->complexity=10; otp->expect_loss=0; otp->lookahead=0; otp->bytes_written=0; otp->pages_out=0; otp->comment_padding=512; otp->eos=0; otp->nb_samples=-1; otp->id=-1; otp->nb_encoded=0; otp->enc_granulepos=0; otp->total_bytes=0; otp->peak_bytes=0; otp->max_ogg_delay=48000; /* 0 dB gain is recommended unless you know what you're doing */ otp->inopt.gain=0; otp->inopt.samplesize=(oport->bytes_per_sample)*8; otp->inopt.endianness=0; otp->inopt.rawmode=0; otp->inopt.ignorelength=0; otp->inopt.copy_comments=1; otp->inopt.skip=0; otp->start_time = time(NULL); srand(((_getpid()&65535)<<15)^(otp->start_time)); otp->serialno=rand(); otp->opus_version="libopus 1.0.3"; /* Vendor string should just be the encoder library, the ENCODER comment specifies the tool used. */ comment_init(&otp->inopt.comments, &otp->inopt.comments_length, otp->opus_version); snprintf(otp->ENCODER_string, sizeof(otp->ENCODER_string), "opusenc from %s %s", PACKAGE_NAME, PACKAGE_VERSION); comment_add(&otp->inopt.comments, &otp->inopt.comments_length, "ENCODER", otp->ENCODER_string); otp->outFile=(char *)filename; otp->rate=otp->inopt.rate; otp->chan=otp->inopt.channels; otp->inopt.skip=0; if(otp->rate>24000)otp->coding_rate=48000; else if(otp->rate>16000)otp->coding_rate=24000; else if(otp->rate>12000)otp->coding_rate=16000; else if(otp->rate>8000)otp->coding_rate=12000; else otp->coding_rate=8000; otp->frame_size=otp->frame_size/(48000/otp->coding_rate); /*OggOpus headers*/ /*FIXME: broke forcemono*/ otp->header.channels=otp->chan; otp->header.channel_mapping=otp->header.channels>8?255:otp->chan>2; otp->header.input_sample_rate=otp->rate; otp->header.gain=otp->inopt.gain; /* Initialize OPUS encoder */ /* Framesizes <10ms can only use the MDCT modes, so we switch on RESTRICTED_LOWDELAY to save the extra 2.5ms of codec lookahead when we'll be using only small frames. */ otp->st=opus_multistream_surround_encoder_create(otp->coding_rate, otp->chan, otp->header.channel_mapping, &otp->header.nb_streams, &otp->header.nb_coupled, otp->header.stream_map, otp->frame_size<480/(48000/otp->coding_rate)?OPUS_APPLICATION_RESTRICTED_LOWDELAY:OPUS_APPLICATION_AUDIO, &ret); if(ret!=OPUS_OK) { fprintf(stderr, "Error cannot create encoder: %s\n", opus_strerror(ret)); exit(1); } otp->min_bytes=otp->max_frame_bytes=(1275*3+7)*otp->header.nb_streams; otp->packet=(unsigned char*)malloc(sizeof(unsigned char)*otp->max_frame_bytes); if(otp->packet==NULL) { fprintf(stderr, "Error allocating packet buffer.\n"); exit(1); } if(otp->bitrate<0) { /* Lower default rate for sampling rates [8000-44100) by a factor of (rate+16k)/(64k) */ otp->bitrate=((64000*otp->header.nb_streams+32000*otp->header.nb_coupled)* (IMIN(48,IMAX(8,((otp->rate<44100?otp->rate:48000)+1000)/1000))+16)+32)>>6; }
int opusEncoder(int argc, char **argv) { static const input_format raw_format = {NULL, 0, raw_open, wav_close, "raw",N_("RAW file reader")}; int option_index=0; struct option long_options[] = { {"quiet", no_argument, NULL, 0}, {"bitrate", required_argument, NULL, 0}, {"hard-cbr",no_argument,NULL, 0}, {"vbr",no_argument,NULL, 0}, {"cvbr",no_argument,NULL, 0}, {"comp", required_argument, NULL, 0}, {"complexity", required_argument, NULL, 0}, {"framesize", required_argument, NULL, 0}, {"expect-loss", required_argument, NULL, 0}, {"downmix-mono",no_argument,NULL, 0}, {"downmix-stereo",no_argument,NULL, 0}, {"no-downmix",no_argument,NULL, 0}, {"max-delay", required_argument, NULL, 0}, {"serial", required_argument, NULL, 0}, {"save-range", required_argument, NULL, 0}, {"set-ctl-int", required_argument, NULL, 0}, {"help", no_argument, NULL, 0}, {"raw", no_argument, NULL, 0}, {"raw-bits", required_argument, NULL, 0}, {"raw-rate", required_argument, NULL, 0}, {"raw-chan", required_argument, NULL, 0}, {"raw-endianness", required_argument, NULL, 0}, {"ignorelength", no_argument, NULL, 0}, {"rate", required_argument, NULL, 0}, {"version", no_argument, NULL, 0}, {"version-short", no_argument, NULL, 0}, {"comment", required_argument, NULL, 0}, {"artist", required_argument, NULL, 0}, {"title", required_argument, NULL, 0}, {"album", required_argument, NULL, 0}, {"date", required_argument, NULL, 0}, {"genre", required_argument, NULL, 0}, {"picture", required_argument, NULL, 0}, {"padding", required_argument, NULL, 0}, {"discard-comments", no_argument, NULL, 0}, {"discard-pictures", no_argument, NULL, 0}, {0, 0, 0, 0} }; int i, ret; int cline_size; OpusMSEncoder *st; const char *opus_version; unsigned char *packet; float *input; /*I/O*/ oe_enc_opt inopt; const input_format *in_format; char *inFile; char *outFile; char *range_file; FILE *fin; FILE *fout; FILE *frange; ogg_stream_state os; ogg_page og; ogg_packet op; ogg_int64_t last_granulepos=0; ogg_int64_t enc_granulepos=0; ogg_int64_t original_samples=0; ogg_int32_t id=-1; int last_segments=0; int eos=0; OpusHeader header; char ENCODER_string[1024]; /*Counters*/ opus_int64 nb_encoded=0; opus_int64 bytes_written=0; opus_int64 pages_out=0; opus_int64 total_bytes=0; opus_int64 total_samples=0; opus_int32 nbBytes; opus_int32 nb_samples; opus_int32 peak_bytes=0; opus_int32 min_bytes; time_t start_time; time_t stop_time; time_t last_spin=0; int last_spin_len=0; /*Settings*/ int quiet=0; int max_frame_bytes; opus_int32 bitrate=-1; opus_int32 rate=48000; opus_int32 coding_rate=48000; opus_int32 frame_size=960; int chan=2; int with_hard_cbr=0; int with_cvbr=0; int expect_loss=0; int complexity=10; int downmix=0; int *opt_ctls_ctlval; int opt_ctls=0; int max_ogg_delay=48000; /*48kHz samples*/ int seen_file_icons=0; int comment_padding=512; int serialno; opus_int32 lookahead=0; #ifdef WIN_UNICODE int argc_utf8; char **argv_utf8; #endif if(query_cpu_support()){ fprintf(stderr,"\n\n** WARNING: This program was compiled with SSE%s\n",query_cpu_support()>1?"2":""); fprintf(stderr," but this CPU claims to lack these instructions. **\n\n"); } #ifdef WIN_UNICODE (void)argc; (void)argv; init_commandline_arguments_utf8(&argc_utf8, &argv_utf8); #endif opt_ctls_ctlval=NULL; frange=NULL; range_file=NULL; in_format=NULL; inopt.channels=chan; inopt.rate=coding_rate=rate; /* 0 dB gain is recommended unless you know what you're doing */ inopt.gain=0; inopt.samplesize=16; inopt.endianness=0; inopt.rawmode=0; inopt.ignorelength=0; inopt.copy_comments=1; inopt.copy_pictures=1; start_time = time(NULL); srand(((getpid()&65535)<<15)^start_time); serialno=rand(); opus_version=opus_get_version_string(); /*Vendor string should just be the encoder library, the ENCODER comment specifies the tool used.*/ comment_init(&inopt.comments, &inopt.comments_length, opus_version); // snprintf(ENCODER_string, sizeof(ENCODER_string), "opusenc from %s %s",PACKAGE_NAME,PACKAGE_VERSION); comment_add(&inopt.comments, &inopt.comments_length, "ENCODER", ENCODER_string); /*Process command-line options*/ cline_size=0; while(1){ int c; int save_cmd=1; c=getopt_long(argc_utf8, argv_utf8, "hV", long_options, &option_index); if(c==-1) break; switch(c){ case 0: if(strcmp(long_options[option_index].name,"quiet")==0){ quiet=1; }else if(strcmp(long_options[option_index].name,"bitrate")==0){ bitrate=atof(optarg)*1000.; }else if(strcmp(long_options[option_index].name,"hard-cbr")==0){ with_hard_cbr=1; with_cvbr=0; }else if(strcmp(long_options[option_index].name,"cvbr")==0){ with_cvbr=1; with_hard_cbr=0; }else if(strcmp(long_options[option_index].name,"vbr")==0){ with_cvbr=0; with_hard_cbr=0; }else if(strcmp(long_options[option_index].name,"help")==0){ usage(); return 0;//exit(0); }else if(strcmp(long_options[option_index].name,"version")==0){ opustoolsversion(opus_version); return 0;//exit(0); }else if(strcmp(long_options[option_index].name,"version-short")==0){ opustoolsversion_short(opus_version); return 0;//exit(0); }else if(strcmp(long_options[option_index].name,"ignorelength")==0){ inopt.ignorelength=1; }else if(strcmp(long_options[option_index].name,"raw")==0){ inopt.rawmode=1; save_cmd=0; }else if(strcmp(long_options[option_index].name,"raw-bits")==0){ inopt.rawmode=1; inopt.samplesize=atoi(optarg); save_cmd=0; if(inopt.samplesize!=8&&inopt.samplesize!=16&&inopt.samplesize!=24){ fprintf(stderr,"Invalid bit-depth: %s\n",optarg); fprintf(stderr,"--raw-bits must be one of 8,16, or 24\n"); return 0;//exit(1); } }else if(strcmp(long_options[option_index].name,"raw-rate")==0){ inopt.rawmode=1; inopt.rate=atoi(optarg); save_cmd=0; }else if(strcmp(long_options[option_index].name,"raw-chan")==0){ inopt.rawmode=1; inopt.channels=atoi(optarg); save_cmd=0; }else if(strcmp(long_options[option_index].name,"raw-endianness")==0){ inopt.rawmode=1; inopt.endianness=atoi(optarg); save_cmd=0; }else if(strcmp(long_options[option_index].name,"downmix-mono")==0){ downmix=1; }else if(strcmp(long_options[option_index].name,"downmix-stereo")==0){ downmix=2; }else if(strcmp(long_options[option_index].name,"no-downmix")==0){ downmix=-1; }else if(strcmp(long_options[option_index].name,"expect-loss")==0){ expect_loss=atoi(optarg); if(expect_loss>100||expect_loss<0){ fprintf(stderr,"Invalid expect-loss: %s\n",optarg); fprintf(stderr,"Expected loss is a percent and must be 0-100.\n"); return 0;//exit(1); } }else if(strcmp(long_options[option_index].name,"comp")==0 || strcmp(long_options[option_index].name,"complexity")==0){ complexity=atoi(optarg); if(complexity>10||complexity<0){ fprintf(stderr,"Invalid complexity: %s\n",optarg); fprintf(stderr,"Complexity must be 0-10.\n"); return 0;//exit(1); } }else if(strcmp(long_options[option_index].name,"framesize")==0){ if(strcmp(optarg,"2.5")==0)frame_size=120; else if(strcmp(optarg,"5")==0)frame_size=240; else if(strcmp(optarg,"10")==0)frame_size=480; else if(strcmp(optarg,"20")==0)frame_size=960; else if(strcmp(optarg,"40")==0)frame_size=1920; else if(strcmp(optarg,"60")==0)frame_size=2880; else{ fprintf(stderr,"Invalid framesize: %s\n",optarg); fprintf(stderr,"Framesize must be 2.5, 5, 10, 20, 40, or 60.\n"); return 0;//exit(1); } }else if(strcmp(long_options[option_index].name,"max-delay")==0){ max_ogg_delay=floor(atof(optarg)*48.); if(max_ogg_delay<0||max_ogg_delay>48000){ fprintf(stderr,"Invalid max-delay: %s\n",optarg); fprintf(stderr,"max-delay 0-1000 ms.\n"); return 0;//exit(1); } }else if(strcmp(long_options[option_index].name,"serial")==0){ serialno=atoi(optarg); }else if(strcmp(long_options[option_index].name,"set-ctl-int")==0){ int len=strlen(optarg),target; char *spos,*tpos; spos=strchr(optarg,'='); if(len<3||spos==NULL||(spos-optarg)<1||(spos-optarg)>=len){ fprintf(stderr, "Invalid set-ctl-int: %s\n", optarg); fprintf(stderr, "Syntax is --set-ctl-int intX=intY or\n"); fprintf(stderr, "Syntax is --set-ctl-int intS:intX=intY\n"); return 0;//exit(1); } tpos=strchr(optarg,':'); if(tpos==NULL){ target=-1; tpos=optarg-1; }else target=atoi(optarg); if((atoi(tpos+1)&1)!=0){ fprintf(stderr, "Invalid set-ctl-int: %s\n", optarg); fprintf(stderr, "libopus set CTL values are even.\n"); return 0;//exit(1); } if(opt_ctls==0)opt_ctls_ctlval=malloc(sizeof(int)*3); else opt_ctls_ctlval=realloc(opt_ctls_ctlval,sizeof(int)*(opt_ctls+1)*3); opt_ctls_ctlval[opt_ctls*3]=target; opt_ctls_ctlval[opt_ctls*3+1]=atoi(tpos+1); opt_ctls_ctlval[opt_ctls*3+2]=atoi(spos+1); opt_ctls++; }else if(strcmp(long_options[option_index].name,"save-range")==0){ frange=fopen_utf8(optarg,"w"); save_cmd=0; if(frange==NULL){ perror(optarg); fprintf(stderr,"Could not open save-range file: %s\n",optarg); fprintf(stderr,"Must provide a writable file name.\n"); return 0;//exit(1); } range_file=optarg; }else if(strcmp(long_options[option_index].name,"comment")==0){ save_cmd=0; if(!strchr(optarg,'=')){ fprintf(stderr, "Invalid comment: %s\n", optarg); fprintf(stderr, "Comments must be of the form name=value\n"); return 0;//exit(1); } comment_add(&inopt.comments, &inopt.comments_length, NULL, optarg); }else if(strcmp(long_options[option_index].name,"artist")==0){ save_cmd=0; comment_add(&inopt.comments, &inopt.comments_length, "artist", optarg); } else if(strcmp(long_options[option_index].name,"title")==0){ save_cmd=0; comment_add(&inopt.comments, &inopt.comments_length, "title", optarg); } else if(strcmp(long_options[option_index].name,"album")==0){ save_cmd=0; comment_add(&inopt.comments, &inopt.comments_length, "album", optarg); } else if(strcmp(long_options[option_index].name,"date")==0){ save_cmd=0; comment_add(&inopt.comments, &inopt.comments_length, "date", optarg); } else if(strcmp(long_options[option_index].name,"genre")==0){ save_cmd=0; comment_add(&inopt.comments, &inopt.comments_length, "genre", optarg); } else if(strcmp(long_options[option_index].name,"picture")==0){ const char *error_message; char *picture_data; save_cmd=0; picture_data=parse_picture_specification(optarg,&error_message, &seen_file_icons); if(picture_data==NULL){ fprintf(stderr,"Error parsing picture option: %s\n",error_message); return 0;//exit(1); } comment_add(&inopt.comments,&inopt.comments_length, "METADATA_BLOCK_PICTURE",picture_data); free(picture_data); } else if(strcmp(long_options[option_index].name,"padding")==0){ comment_padding=atoi(optarg); } else if(strcmp(long_options[option_index].name,"discard-comments")==0){ inopt.copy_comments=0; inopt.copy_pictures=0; } else if(strcmp(long_options[option_index].name,"discard-pictures")==0){ inopt.copy_pictures=0; } /*Commands whos arguments would leak file paths or just end up as metadata should have save_cmd=0; to prevent them from being saved in the command-line tag.*/ break; case 'h': usage(); return 0;//exit(0); break; case 'V': opustoolsversion(opus_version); return 0;//exit(0); break; case '?': usage(); return 0;//exit(1); break; } if(save_cmd && cline_size<(int)sizeof(ENCODER_string)){ ret=snprintf(&ENCODER_string[cline_size], sizeof(ENCODER_string)-cline_size, "%s--%s",cline_size==0?"":" ",long_options[option_index].name); if(ret<0||ret>=((int)sizeof(ENCODER_string)-cline_size)){ cline_size=sizeof(ENCODER_string); } else { cline_size+=ret; if(optarg){ ret=snprintf(&ENCODER_string[cline_size], sizeof(ENCODER_string)-cline_size, " %s",optarg); if(ret<0||ret>=((int)sizeof(ENCODER_string)-cline_size)){ cline_size=sizeof(ENCODER_string); } else { cline_size+=ret; } } } } } if(argc_utf8-optind!=2){ usage(); return 0;//exit(1); } inFile=argv_utf8[optind]; outFile=argv_utf8[optind+1]; if(cline_size>0)comment_add(&inopt.comments, &inopt.comments_length, "ENCODER_OPTIONS", ENCODER_string); if(strcmp(inFile, "-")==0){ #if defined WIN32 || defined _WIN32 _setmode(_fileno(stdin), _O_BINARY); #elif defined OS2 _fsetmode(stdin,"b"); #endif fin=stdin; }else{ fin=fopen_utf8(inFile, "rb"); if(!fin){ perror(inFile); return 0;//exit(1); } } if(inopt.rawmode){ in_format = &raw_format; in_format->open_func(fin, &inopt, NULL, 0); }else in_format=open_audio_file(fin,&inopt); if(!in_format){ fprintf(stderr,"Error parsing input file: %s\n",inFile); return 0;//exit(1); } if(downmix==0&&inopt.channels>2&&bitrate>0&&bitrate<(16000*inopt.channels)){ if(!quiet)fprintf(stderr,"Notice: Surround bitrate less than 16kbit/sec/channel, downmixing.\n"); downmix=inopt.channels>8?1:2; } if(downmix>0&&downmix<inopt.channels)downmix=setup_downmix(&inopt,downmix); else downmix=0; rate=inopt.rate; chan=inopt.channels; inopt.skip=0; /*In order to code the complete length we'll need to do a little padding*/ setup_padder(&inopt,&original_samples); if(rate>24000)coding_rate=48000; else if(rate>16000)coding_rate=24000; else if(rate>12000)coding_rate=16000; else if(rate>8000)coding_rate=12000; else coding_rate=8000; frame_size=frame_size/(48000/coding_rate); /*Scale the resampler complexity, but only for 48000 output because the near-cutoff behavior matters a lot more at lower rates.*/ //if(rate!=coding_rate)setup_resample(&inopt,coding_rate==48000?(complexity+1)/2:5,coding_rate); if(rate!=coding_rate&&complexity!=10&&!quiet){ fprintf(stderr,"Notice: Using resampling with complexity<10.\n"); fprintf(stderr,"Opusenc is fastest with 48, 24, 16, 12, or 8kHz input.\n\n"); } /*OggOpus headers*/ /*FIXME: broke forcemono*/ header.channels=chan; header.channel_mapping=header.channels>8?255:chan>2; header.input_sample_rate=rate; header.gain=inopt.gain; /*Initialize OPUS encoder*/ /*Framesizes <10ms can only use the MDCT modes, so we switch on RESTRICTED_LOWDELAY to save the extra 2.5ms of codec lookahead when we'll be using only small frames.*/ st=opus_multistream_surround_encoder_create(coding_rate, chan, header.channel_mapping, &header.nb_streams, &header.nb_coupled, header.stream_map, frame_size<480/(48000/coding_rate)?OPUS_APPLICATION_RESTRICTED_LOWDELAY:OPUS_APPLICATION_AUDIO, &ret); if(ret!=OPUS_OK){ fprintf(stderr, "Error cannot create encoder: %s\n", opus_strerror(ret)); return 0;//exit(1); } min_bytes=max_frame_bytes=(1275*3+7)*header.nb_streams; packet=malloc(sizeof(unsigned char)*max_frame_bytes); if(packet==NULL){ fprintf(stderr,"Error allocating packet buffer.\n"); return 0;//exit(1); } if(bitrate<0){ /*Lower default rate for sampling rates [8000-44100) by a factor of (rate+16k)/(64k)*/ bitrate=((64000*header.nb_streams+32000*header.nb_coupled)* (IMIN(48,IMAX(8,((rate<44100?rate:48000)+1000)/1000))+16)+32)>>6; }
void OggOpusFile::Init() { inopt.skip=0; //In our case, its 160 samples/frame. Changing rate to anything rather than 8000Hz may require significant change in the way of handle number of samples to fix the encoder frame_size=frame_size/(48000/coding_rate); /*OggOpus headers*/ /*FIXME: broke forcemono*/ header.channels=chan; header.channel_mapping= 0;//header.channels>8?255:chan>2; //=0 for wav header.input_sample_rate=rate; header.gain=inopt.gain; //=0 here st=opus_multistream_surround_encoder_create(coding_rate, chan, header.channel_mapping, &header.nb_streams, &header.nb_coupled, header.stream_map, frame_size<480/(48000/coding_rate)?OPUS_APPLICATION_RESTRICTED_LOWDELAY:OPUS_APPLICATION_AUDIO, &ret); if(ret!=OPUS_OK){ fprintf(stderr, "Error cannot create encoder: %s\n", opus_strerror(ret)); exit(1); } max_frame_bytes=(1275*3+7)*header.nb_streams; min_bytes=max_frame_bytes; m_outBuf = (unsigned char*)malloc(max_frame_bytes); if(m_outBuf==NULL){ fprintf(stderr,"Error allocating m_outBuf buffer.\n"); exit(1); } //We would set bitrate configurable as low as 6k //Or use the provided formulae to calculate a optimized bitrate if(bitrate<0){ /*Lower default rate for sampling rates [8000-44100) by a factor of (rate+16k)/(64k)*/ // bitrate=((64000*header.nb_streams+32000*header.nb_coupled)* // (IMIN(48,IMAX(8,((rate<44100?rate:48000)+1000)/1000))+16)+32)>>6; bitrate=6000; } if(bitrate>(1024000*chan)||bitrate<500){ fprintf(stderr,"Error: Bitrate %d bits/sec is insane.\nDid you mistake bits for kilobits?\n",bitrate); fprintf(stderr,"--bitrate values from 6-256 kbit/sec per channel are meaningful.\n"); exit(1); } bitrate=IMIN(chan*256000,bitrate); ret=opus_multistream_encoder_ctl(st, OPUS_SET_BITRATE(bitrate)); if(ret!=OPUS_OK){ fprintf(stderr,"Error OPUS_SET_BITRATE returned: %s\n",opus_strerror(ret)); exit(1); } ret=opus_multistream_encoder_ctl(st, OPUS_SET_VBR(!with_hard_cbr)); if(ret!=OPUS_OK){ fprintf(stderr,"Error OPUS_SET_VBR returned: %s\n",opus_strerror(ret)); exit(1); } if(!with_hard_cbr){ ret=opus_multistream_encoder_ctl(st, OPUS_SET_VBR_CONSTRAINT(with_cvbr)); if(ret!=OPUS_OK){ fprintf(stderr,"Error OPUS_SET_VBR_CONSTRAINT returned: %s\n",opus_strerror(ret)); exit(1); } } ret=opus_multistream_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity)); if(ret!=OPUS_OK){ fprintf(stderr,"Error OPUS_SET_COMPLEXITY returned: %s\n",opus_strerror(ret)); exit(1); } ret=opus_multistream_encoder_ctl(st, OPUS_SET_PACKET_LOSS_PERC(expect_loss)); if(ret!=OPUS_OK){ fprintf(stderr,"Error OPUS_SET_PACKET_LOSS_PERC returned: %s\n",opus_strerror(ret)); exit(1); } #ifdef OPUS_SET_LSB_DEPTH ret=opus_multistream_encoder_ctl(st, OPUS_SET_LSB_DEPTH(IMAX(8,IMIN(24,inopt.samplesize)))); if(ret!=OPUS_OK){ fprintf(stderr,"Warning OPUS_SET_LSB_DEPTH returned: %s\n",opus_strerror(ret)); } #endif for(i=0;i<opt_ctls;i++){ int target=opt_ctls_ctlval[i*3]; if(target==-1){ ret=opus_multistream_encoder_ctl(st,opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2]); if(ret!=OPUS_OK){ fprintf(stderr,"Error opus_multistream_encoder_ctl(st,%d,%d) returned: %s\n",opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2],opus_strerror(ret)); exit(1); } }else if(target<header.nb_streams){ OpusEncoder *oe; opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(target,&oe)); ret=opus_encoder_ctl(oe, opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2]); if(ret!=OPUS_OK){ fprintf(stderr,"Error opus_encoder_ctl(st[%d],%d,%d) returned: %s\n",target,opt_ctls_ctlval[i*3+1],opt_ctls_ctlval[i*3+2],opus_strerror(ret)); exit(1); } }else{ fprintf(stderr,"Error --set-ctl-int target stream %d is higher than the maximum stream number %d.\n",target,header.nb_streams-1); exit(1); } } /*We do the lookahead check late so user CTLs can change it*/ ret=opus_multistream_encoder_ctl(st, OPUS_GET_LOOKAHEAD(&lookahead)); if(ret!=OPUS_OK){ fprintf(stderr,"Error OPUS_GET_LOOKAHEAD returned: %s\n",opus_strerror(ret)); exit(1); } inopt.skip+=lookahead; /*Regardless of the rate we're coding at the ogg timestamping/skip is always timed at 48000.*/ header.preskip=inopt.skip*(48000./coding_rate); /* Extra samples that need to be read to compensate for the pre-skip */ inopt.extraout=(int)header.preskip*(rate/48000.); /*Initialize Ogg stream struct*/ if(ogg_stream_init(&os, serialno)==-1){ fprintf(stderr,"Error: stream init failed\n"); exit(1); } int opus_app; fprintf(stderr,"Encoding using 1.1"); opus_multistream_encoder_ctl(st,OPUS_GET_APPLICATION(&opus_app)); if(opus_app==OPUS_APPLICATION_VOIP)fprintf(stderr," (VoIP)\n"); else if(opus_app==OPUS_APPLICATION_AUDIO)fprintf(stderr," (audio)\n"); else if(opus_app==OPUS_APPLICATION_RESTRICTED_LOWDELAY)fprintf(stderr," (low-delay)\n"); else fprintf(stderr," (unknown)\n"); fprintf(stderr,"-----------------------------------------------------\n"); fprintf(stderr," Input: %0.6gkHz %d channel%s\n", header.input_sample_rate/1000.,chan,chan<2?"":"s"); fprintf(stderr," Output: %d channel%s (",header.channels,header.channels<2?"":"s"); if(header.nb_coupled>0)fprintf(stderr,"%d coupled",header.nb_coupled*2); if(header.nb_streams-header.nb_coupled>0)fprintf(stderr, "%s%d uncoupled",header.nb_coupled>0?", ":"", header.nb_streams-header.nb_coupled); fprintf(stderr,")\n %0.2gms packets, %0.6gkbit/sec%s\n", frame_size/(coding_rate/1000.), bitrate/1000., with_hard_cbr?" CBR":with_cvbr?" CVBR":" VBR"); fprintf(stderr," Preskip: %d\n",header.preskip); }
int convert( const char* source_path, const char* target_path, int bitRate, int quality) { static const input_format raw_format = {NULL, 0, raw_open, wav_close, "raw",N_("RAW file reader")}; int i, ret; int cline_size; OpusMSEncoder *st; const char *opus_version; unsigned char *packet; #ifdef FIXED_POINT opus_int16 *input; #else float *input; #endif /*I/O*/ oe_enc_opt inopt; const input_format *in_format; char *range_file; FILE *fin; FILE *fout; FILE *frange; ogg_stream_state os; ogg_page og; ogg_packet op; ogg_int64_t last_granulepos=0; ogg_int64_t enc_granulepos=0; ogg_int64_t original_samples=0; ogg_int32_t id=-1; int last_segments=0; int eos=0; OpusHeader header; char ENCODER_string[1024]; /*Counters*/ opus_int64 nb_encoded=0; opus_int64 bytes_written=0; opus_int64 pages_out=0; opus_int64 total_bytes=0; opus_int64 total_samples=0; opus_int32 nbBytes; opus_int32 nb_samples; opus_int32 peak_bytes=0; opus_int32 min_bytes; time_t start_time; time_t stop_time; time_t last_spin=0; int last_spin_len=0; /*Settings*/ int quiet=0; int max_frame_bytes; opus_int32 bitrate=bitRate * 1000; opus_int32 rate=48000; opus_int32 coding_rate=48000; opus_int32 frame_size=960; int chan=2; int with_hard_cbr=0; int with_cvbr=0; int expect_loss=0; int complexity=10 - quality; // 10 -- best int downmix=0; int *opt_ctls_ctlval; int opt_ctls=0; int max_ogg_delay=48000; // 48kHz samples int seen_file_icons=0; int comment_padding=512; int serialno; opus_int32 lookahead=0; opt_ctls_ctlval=NULL; frange=NULL; range_file=NULL; in_format=NULL; inopt.channels=chan; inopt.rate=coding_rate=rate; // 0 dB gain is recommended unless you know what you're doing inopt.gain=0; inopt.samplesize=16; inopt.endianness=0; inopt.rawmode=0; inopt.ignorelength=0; inopt.copy_comments=1; start_time = time(NULL); srand(((getpid()&65535)<<15)^start_time); serialno=rand(); opus_version= "libopus 1.1.1"; //opus_get_version_string(); // Vendor string should just be the encoder library, the ENCODER comment specifies the tool used. if( comment_init(&inopt.comments, &inopt.comments_length, opus_version) ) { return 1; // failed } snprintf(ENCODER_string, sizeof(ENCODER_string), "%s %s",PACKAGE_NAME,PACKAGE_VERSION); if( comment_add(&inopt.comments, &inopt.comments_length, "ENCODER", ENCODER_string) ) { return 1; // failed } if(cline_size>0){ if( comment_add(&inopt.comments, &inopt.comments_length, "ENCODER_OPTIONS", ENCODER_string) ) { return 1; // failed } } fin=fopen_utf8(source_path, "rb"); if(!fin){ perror(source_path); return 1; } if(inopt.rawmode){ in_format = &raw_format; in_format->open_func(fin, &inopt, NULL, 0); }else in_format=open_audio_file(fin,&inopt); if(!in_format){ LOGD("Error parsing input file: %s\n",source_path); return 1; } if(downmix==0&&inopt.channels>2&&bitrate>0&&bitrate<(16000*inopt.channels)){ if(!quiet)LOGD("Notice: Surround bitrate less than 16kbit/sec/channel, downmixing.\n"); downmix=inopt.channels>8?1:2; } if(downmix>0&&downmix<inopt.channels)downmix=setup_downmix(&inopt,downmix); else downmix=0; rate=inopt.rate; chan=inopt.channels; inopt.skip=0; // In order to code the complete length we'll need to do a little padding setup_padder(&inopt,&original_samples); if(rate>24000)coding_rate=48000; else if(rate>16000)coding_rate=24000; else if(rate>12000)coding_rate=16000; else if(rate>8000)coding_rate=12000; else coding_rate=8000; frame_size=frame_size/(48000/coding_rate); // Scale the resampler complexity, but only for 48000 output because // the near-cutoff behavior matters a lot more at lower rates. // if(rate!=coding_rate)setup_resample(&inopt,coding_rate==48000?(complexity+1)/2:5,coding_rate); if(rate!=coding_rate&&complexity!=10&&!quiet){ LOGD("Notice: Using resampling with complexity<10.\n"); LOGD("Opusenc is fastest with 48, 24, 16, 12, or 8kHz input.\n\n"); } // OggOpus headers // FIXME: broke forcemono header.channels=chan; header.channel_mapping=header.channels>8?255:chan>2; header.input_sample_rate=rate; header.gain=inopt.gain; // Initialize OPUS encoder st=opus_multistream_surround_encoder_create(coding_rate, chan, header.channel_mapping, &header.nb_streams, &header.nb_coupled, header.stream_map, OPUS_APPLICATION_AUDIO, &ret); if(ret!=OPUS_OK){ LOGD( "Error cannot create encoder: %s\n", opus_strerror(ret)); return 1; } min_bytes=max_frame_bytes=(1275*3+7)*header.nb_streams; packet=malloc(sizeof(unsigned char)*max_frame_bytes); if(packet==NULL){ LOGD("Error allocating packet buffer.\n"); return 1; } if(bitrate<0){ // Lower default rate for sampling rates [8000-44100) by a factor of (rate+16k)/(64k) bitrate=((64000*header.nb_streams+32000*header.nb_coupled)* (IMIN(48,IMAX(8,((rate<44100?rate:48000)+1000)/1000))+16)+32)>>6; }