static gboolean gst_opus_enc_setup (GstOpusEnc * enc) { int error = OPUS_OK; #ifndef GST_DISABLE_DEBUG GST_DEBUG_OBJECT (enc, "setup: %d Hz, %d channels, %d stereo streams, family %d", enc->sample_rate, enc->n_channels, enc->n_stereo_streams, enc->channel_mapping_family); GST_INFO_OBJECT (enc, "Mapping tables built: %d channels, %d stereo streams", enc->n_channels, enc->n_stereo_streams); gst_opus_common_log_channel_mapping_table (GST_ELEMENT (enc), opusenc_debug, "Encoding mapping table", enc->n_channels, enc->encoding_channel_mapping); gst_opus_common_log_channel_mapping_table (GST_ELEMENT (enc), opusenc_debug, "Decoding mapping table", enc->n_channels, enc->decoding_channel_mapping); #endif enc->state = opus_multistream_encoder_create (enc->sample_rate, enc->n_channels, enc->n_channels - enc->n_stereo_streams, enc->n_stereo_streams, enc->encoding_channel_mapping, enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP, &error); if (!enc->state || error != OPUS_OK) goto encoder_creation_failed; opus_multistream_encoder_ctl (enc->state, OPUS_SET_BITRATE (enc->bitrate), 0); opus_multistream_encoder_ctl (enc->state, OPUS_SET_BANDWIDTH (enc->bandwidth), 0); opus_multistream_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr), 0); opus_multistream_encoder_ctl (enc->state, OPUS_SET_VBR_CONSTRAINT (enc->constrained_vbr), 0); opus_multistream_encoder_ctl (enc->state, OPUS_SET_COMPLEXITY (enc->complexity), 0); opus_multistream_encoder_ctl (enc->state, OPUS_SET_INBAND_FEC (enc->inband_fec), 0); opus_multistream_encoder_ctl (enc->state, OPUS_SET_DTX (enc->dtx), 0); opus_multistream_encoder_ctl (enc->state, OPUS_SET_PACKET_LOSS_PERC (enc->packet_loss_percentage), 0); GST_LOG_OBJECT (enc, "we have frame size %d", enc->frame_size); return TRUE; encoder_creation_failed: GST_ERROR_OBJECT (enc, "Encoder creation failed"); return FALSE; }
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; }
static void gst_opus_enc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstOpusEnc *enc; enc = GST_OPUS_ENC (object); #define GST_OPUS_UPDATE_PROPERTY(prop,type,ctl) do { \ g_mutex_lock (enc->property_lock); \ enc->prop = g_value_get_##type (value); \ if (enc->state) { \ opus_multistream_encoder_ctl (enc->state, OPUS_SET_##ctl (enc->prop)); \ } \ g_mutex_unlock (enc->property_lock); \ } while(0) switch (prop_id) { case PROP_AUDIO: enc->audio_or_voip = g_value_get_boolean (value); break; case PROP_BITRATE: GST_OPUS_UPDATE_PROPERTY (bitrate, int, BITRATE); break; case PROP_BANDWIDTH: GST_OPUS_UPDATE_PROPERTY (bandwidth, enum, BANDWIDTH); break; case PROP_FRAME_SIZE: g_mutex_lock (enc->property_lock); enc->frame_size = g_value_get_enum (value); enc->frame_samples = gst_opus_enc_get_frame_samples (enc); gst_opus_enc_setup_base_class (enc, GST_AUDIO_ENCODER (enc)); g_mutex_unlock (enc->property_lock); break; case PROP_CBR: /* this one has an opposite meaning to the opus ctl... */ g_mutex_lock (enc->property_lock); enc->cbr = g_value_get_boolean (value); opus_multistream_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr)); g_mutex_unlock (enc->property_lock); break; case PROP_CONSTRAINED_VBR: GST_OPUS_UPDATE_PROPERTY (constrained_vbr, boolean, VBR_CONSTRAINT); break; case PROP_COMPLEXITY: GST_OPUS_UPDATE_PROPERTY (complexity, int, COMPLEXITY); break; case PROP_INBAND_FEC: GST_OPUS_UPDATE_PROPERTY (inband_fec, boolean, INBAND_FEC); break; case PROP_DTX: GST_OPUS_UPDATE_PROPERTY (dtx, boolean, DTX); break; case PROP_PACKET_LOSS_PERCENT: GST_OPUS_UPDATE_PROPERTY (packet_loss_percentage, int, PACKET_LOSS_PERC); break; case PROP_MAX_PAYLOAD_SIZE: g_mutex_lock (enc->property_lock); enc->max_payload_size = g_value_get_uint (value); g_mutex_unlock (enc->property_lock); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } #undef GST_OPUS_UPDATE_PROPERTY }
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; }
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; }
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); }
krad_opus_t *krad_opus_encoder_create (int channels, int input_sample_rate, int bitrate, int application) { int c; krad_opus_t *krad_opus = calloc(1, sizeof(krad_opus_t)); krad_opus->opus_header = calloc(1, sizeof(OpusHeader)); krad_opus->input_sample_rate = input_sample_rate; krad_opus->channels = channels; krad_opus->bitrate = bitrate; krad_opus->application = application; krad_opus->complexity = DEFAULT_OPUS_COMPLEXITY; krad_opus->signal = OPUS_AUTO; krad_opus->frame_size = DEFAULT_OPUS_FRAME_SIZE; krad_opus->bandwidth = OPUS_BANDWIDTH_FULLBAND; krad_opus->new_frame_size = krad_opus->frame_size; krad_opus->new_complexity = krad_opus->complexity; krad_opus->new_bitrate = krad_opus->bitrate; krad_opus->new_signal = krad_opus->signal; krad_opus->new_bandwidth = krad_opus->bandwidth; for (c = 0; c < krad_opus->channels; c++) { krad_opus->ringbuf[c] = krad_ringbuffer_create (RINGBUFFER_SIZE); krad_opus->resampled_ringbuf[c] = krad_ringbuffer_create (RINGBUFFER_SIZE); krad_opus->samples[c] = malloc(16 * 8192); krad_opus->resampled_samples[c] = malloc(16 * 8192); krad_opus->src_resampler[c] = src_new (KRAD_OPUS_SRC_QUALITY, 1, &krad_opus->src_error[c]); if (krad_opus->src_resampler[c] == NULL) { failfast ("Krad Opus Encoder: src resampler error: %s", src_strerror (krad_opus->src_error[c])); } krad_opus->src_data[c].src_ratio = 48000.0 / krad_opus->input_sample_rate; } if (krad_opus->channels < 3) { krad_opus->streams = 1; if (krad_opus->channels == 2) { krad_opus->coupled_streams = 1; krad_opus->mapping[0] = 0; krad_opus->mapping[1] = 1; } else { krad_opus->coupled_streams = 0; krad_opus->mapping[0] = 0; } krad_opus->opus_header->channel_mapping = 0; } else { krad_opus->opus_header->channel_mapping = 1; switch (krad_opus->channels) { case 3: krad_opus->streams = 2; krad_opus->coupled_streams = 1; krad_opus->mapping[0] = 0; krad_opus->mapping[1] = 1; krad_opus->mapping[2] = 2; case 4: krad_opus->streams = 2; krad_opus->coupled_streams = 2; krad_opus->mapping[0] = 0; krad_opus->mapping[1] = 1; krad_opus->mapping[2] = 2; krad_opus->mapping[3] = 3; case 5: krad_opus->streams = 3; krad_opus->coupled_streams = 2; krad_opus->mapping[0] = 0; krad_opus->mapping[1] = 4; krad_opus->mapping[2] = 1; krad_opus->mapping[3] = 2; krad_opus->mapping[4] = 3; case 6: krad_opus->streams = 4; krad_opus->coupled_streams = 2; krad_opus->mapping[0] = 0; krad_opus->mapping[1] = 4; krad_opus->mapping[2] = 1; krad_opus->mapping[3] = 2; krad_opus->mapping[4] = 3; krad_opus->mapping[5] = 5; case 7: krad_opus->streams = 5; krad_opus->coupled_streams = 2; krad_opus->mapping[0] = 0; krad_opus->mapping[1] = 4; krad_opus->mapping[2] = 1; krad_opus->mapping[3] = 2; krad_opus->mapping[4] = 3; krad_opus->mapping[5] = 5; krad_opus->mapping[6] = 6; case 8: krad_opus->streams = 5; krad_opus->coupled_streams = 3; krad_opus->mapping[0] = 0; krad_opus->mapping[1] = 6; krad_opus->mapping[2] = 1; krad_opus->mapping[3] = 2; krad_opus->mapping[4] = 3; krad_opus->mapping[5] = 4; krad_opus->mapping[6] = 5; krad_opus->mapping[7] = 7; } } krad_opus->opus_header->channels = krad_opus->channels; krad_opus->opus_header->nb_streams = krad_opus->streams; krad_opus->opus_header->nb_coupled = krad_opus->coupled_streams; memcpy (krad_opus->opus_header->stream_map, krad_opus->mapping, 256); krad_opus->encoder = opus_multistream_encoder_create (48000, krad_opus->channels, krad_opus->streams, krad_opus->coupled_streams, krad_opus->mapping, krad_opus->application, &krad_opus->err); if (krad_opus->err != OPUS_OK) { failfast ("Krad Opus Encoder: Cannot create encoder: %s", opus_strerror (krad_opus->err)); } opus_multistream_encoder_ctl (krad_opus->encoder, OPUS_GET_LOOKAHEAD (&krad_opus->lookahead)); krad_opus->opus_header->preskip = krad_opus->lookahead; krad_opus->opus_header->gain = 0; krad_opus->opus_header->input_sample_rate = 48000; if (opus_multistream_encoder_ctl (krad_opus->encoder, OPUS_SET_BITRATE (krad_opus->bitrate)) != OPUS_OK) { failfast ("Krad Opus Encoder: bitrate request failed"); } krad_opus->header_data_size = opus_header_to_packet (krad_opus->opus_header, krad_opus->header_data, 100); krad_opus->krad_codec_header.codec = OPUS; krad_opus->krad_codec_header.header[0] = krad_opus->header_data; krad_opus->krad_codec_header.header_size[0] = krad_opus->header_data_size; krad_opus->krad_codec_header.header_combined = krad_opus->header_data; krad_opus->krad_codec_header.header_combined_size = krad_opus->header_data_size; krad_opus->krad_codec_header.header_count = 2; krad_opus->krad_codec_header.header_size[1] = 8 + 4 + strlen (opus_get_version_string ()) + 4 + 4 + strlen ("ENCODER=") + strlen (APPVERSION); krad_opus->opustags_header = calloc (1, krad_opus->krad_codec_header.header_size[1]); memcpy (krad_opus->opustags_header, "OpusTags", 8); krad_opus->opustags_header[8] = strlen (opus_get_version_string ()); memcpy (krad_opus->opustags_header + 12, opus_get_version_string (), strlen (opus_get_version_string ())); krad_opus->opustags_header[12 + strlen (opus_get_version_string ())] = 1; krad_opus->opustags_header[12 + strlen (opus_get_version_string ()) + 4] = strlen ("ENCODER=") + strlen (APPVERSION); memcpy (krad_opus->opustags_header + 12 + strlen (opus_get_version_string ()) + 4 + 4, "ENCODER=", strlen ("ENCODER=")); memcpy (krad_opus->opustags_header + 12 + strlen (opus_get_version_string ()) + 4 + 4 + strlen ("ENCODER="), APPVERSION, strlen (APPVERSION)); krad_opus->krad_codec_header.header[1] = krad_opus->opustags_header; return krad_opus; }
int krad_opus_encoder_read (krad_opus_t *krad_opus, unsigned char *buffer, int *nframes) { int ready; int bytes; int resp; int s, c; while (krad_ringbuffer_read_space (krad_opus->ringbuf[krad_opus->channels - 1]) >= 512 * 4 ) { for (c = 0; c < krad_opus->channels; c++) { krad_opus->ret = krad_ringbuffer_peek (krad_opus->ringbuf[c], (char *)krad_opus->samples[c], (512 * 4) ); krad_opus->src_data[c].data_in = krad_opus->samples[c]; krad_opus->src_data[c].input_frames = 512; krad_opus->src_data[c].data_out = krad_opus->resampled_samples[c]; krad_opus->src_data[c].output_frames = 2048; krad_opus->src_error[c] = src_process (krad_opus->src_resampler[c], &krad_opus->src_data[c]); if (krad_opus->src_error[c] != 0) { failfast ("Krad Opus Encoder: src resampler error: %s\n", src_strerror(krad_opus->src_error[c])); } krad_ringbuffer_read_advance (krad_opus->ringbuf[c], (krad_opus->src_data[c].input_frames_used * 4) ); krad_opus->ret = krad_ringbuffer_write (krad_opus->resampled_ringbuf[c], (char *)krad_opus->resampled_samples[c], (krad_opus->src_data[c].output_frames_gen * 4) ); } } if (krad_opus->new_bitrate != krad_opus->bitrate) { krad_opus->bitrate = krad_opus->new_bitrate; resp = opus_multistream_encoder_ctl (krad_opus->encoder, OPUS_SET_BITRATE(krad_opus->bitrate)); if (resp != OPUS_OK) { failfast ("Krad Opus Encoder: bitrate request failed %s\n", opus_strerror (resp)); } else { printk ("Krad Opus Encoder: set opus bitrate %d\n", krad_opus->bitrate); } } if (krad_opus->new_frame_size != krad_opus->frame_size) { krad_opus->frame_size = krad_opus->new_frame_size; printk ("Krad Opus Encoder: frame size is now %d\n", krad_opus->frame_size); } if (krad_opus->new_complexity != krad_opus->complexity) { krad_opus->complexity = krad_opus->new_complexity; resp = opus_multistream_encoder_ctl (krad_opus->encoder, OPUS_SET_COMPLEXITY(krad_opus->complexity)); if (resp != OPUS_OK) { failfast ("Krad Opus Encoder: complexity request failed %s. \n", opus_strerror(resp)); } else { printk ("Krad Opus Encoder: set opus complexity %d\n", krad_opus->complexity); } } if (krad_opus->new_signal != krad_opus->signal) { krad_opus->signal = krad_opus->new_signal; resp = opus_multistream_encoder_ctl (krad_opus->encoder, OPUS_SET_SIGNAL(krad_opus->signal)); if (resp != OPUS_OK) { failfast ("Krad Opus Encoder: signal request failed %s\n", opus_strerror(resp)); } else { printk ("Krad Opus Encoder: set opus signal mode %d\n", krad_opus->signal); } } if (krad_opus->new_bandwidth != krad_opus->bandwidth) { krad_opus->bandwidth = krad_opus->new_bandwidth; resp = opus_multistream_encoder_ctl (krad_opus->encoder, OPUS_SET_BANDWIDTH(krad_opus->bandwidth)); if (resp != OPUS_OK) { failfast ("Krad Opus Encoder: bandwidth request failed %s\n", opus_strerror(resp)); } else { printk ("Krad Opus Encoder: Set Opus bandwidth mode %d\n", krad_opus->bandwidth); } } ready = 1; for (c = 0; c < krad_opus->channels; c++) { if (krad_ringbuffer_read_space (krad_opus->resampled_ringbuf[c]) < krad_opus->frame_size * 4) { ready = 0; } } if (ready == 1) { for (c = 0; c < krad_opus->channels; c++) { krad_opus->ret = krad_ringbuffer_read (krad_opus->resampled_ringbuf[c], (char *)krad_opus->resampled_samples[c], (krad_opus->frame_size * 4) ); } for (s = 0; s < krad_opus->frame_size; s++) { for (c = 0; c < krad_opus->channels; c++) { krad_opus->interleaved_resampled_samples[s * krad_opus->channels + c] = krad_opus->resampled_samples[c][s]; } } bytes = opus_multistream_encode_float (krad_opus->encoder, krad_opus->interleaved_resampled_samples, krad_opus->frame_size, buffer, krad_opus->frame_size * 2); if (bytes < 0) { failfast ("Krad Opus Encoding failed: %s.", opus_strerror (bytes)); } *nframes = krad_opus->frame_size; return bytes; } return 0; }