int opus_multistream_encode_float( OpusMSEncoder *st, const float *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes ) { int i, ret; VARDECL(opus_int16, in); ALLOC_STACK; ALLOC(in, frame_size*st->layout.nb_channels, opus_int16); for (i=0;i<frame_size*st->layout.nb_channels;i++) in[i] = FLOAT2INT16(pcm[i]); ret = opus_multistream_encode(st, in, frame_size, data, max_data_bytes); RESTORE_STACK; return ret; }
static GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) { guint8 *bdata, *data, *mdata = NULL; gsize bsize, size; gsize bytes; gint ret = GST_FLOW_OK; gint outsize; GstBuffer *outbuf; g_mutex_lock (enc->property_lock); bytes = enc->frame_samples * enc->n_channels * 2; if (G_LIKELY (buf)) { bdata = GST_BUFFER_DATA (buf); bsize = GST_BUFFER_SIZE (buf); if (G_UNLIKELY (bsize % bytes)) { GST_DEBUG_OBJECT (enc, "draining; adding silence samples"); size = ((bsize / bytes) + 1) * bytes; mdata = g_malloc0 (size); memcpy (mdata, bdata, bsize); bdata = NULL; data = mdata; } else { data = bdata; size = bsize; } } else { GST_DEBUG_OBJECT (enc, "nothing to drain"); goto done; } g_assert (size == bytes); ret = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), GST_BUFFER_OFFSET_NONE, enc->max_payload_size * enc->n_channels, GST_PAD_CAPS (GST_AUDIO_ENCODER_SRC_PAD (enc)), &outbuf); if (GST_FLOW_OK != ret) goto done; GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", enc->frame_samples, (int) bytes); outsize = opus_multistream_encode (enc->state, (const gint16 *) data, enc->frame_samples, GST_BUFFER_DATA (outbuf), enc->max_payload_size * enc->n_channels); if (outsize < 0) { GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); ret = GST_FLOW_ERROR; goto done; } else if (outsize > enc->max_payload_size) { GST_WARNING_OBJECT (enc, "Encoded size %d is higher than max payload size (%d bytes)", outsize, enc->max_payload_size); ret = GST_FLOW_ERROR; goto done; } GST_DEBUG_OBJECT (enc, "Output packet is %u bytes", outsize); GST_BUFFER_SIZE (outbuf) = outsize; ret = gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (enc), outbuf, enc->frame_samples); done: g_mutex_unlock (enc->property_lock); if (mdata) g_free (mdata); return ret; }
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 GstFlowReturn gst_opus_enc_encode (GstOpusEnc * enc, GstBuffer * buf) { guint8 *bdata = NULL, *data, *mdata = NULL; gsize bsize, size; gsize bytes = enc->frame_samples * enc->n_channels * 2; gint ret = GST_FLOW_OK; GstMapInfo map; GstMapInfo omap; gint outsize; GstBuffer *outbuf; g_mutex_lock (enc->property_lock); if (G_LIKELY (buf)) { gst_buffer_map (buf, &map, GST_MAP_READ); bdata = map.data; bsize = map.size; if (G_UNLIKELY (bsize % bytes)) { GST_DEBUG_OBJECT (enc, "draining; adding silence samples"); size = ((bsize / bytes) + 1) * bytes; mdata = g_malloc0 (size); memcpy (mdata, bdata, bsize); data = mdata; } else { data = bdata; size = bsize; } } else { GST_DEBUG_OBJECT (enc, "nothing to drain"); goto done; } g_assert (size == bytes); outbuf = gst_buffer_new_and_alloc (enc->max_payload_size * enc->n_channels); if (!outbuf) goto done; GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", enc->frame_samples, (int) bytes); gst_buffer_map (outbuf, &omap, GST_MAP_WRITE); GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", enc->frame_samples, (int) bytes); outsize = opus_multistream_encode (enc->state, (const gint16 *) data, enc->frame_samples, omap.data, enc->max_payload_size * enc->n_channels); gst_buffer_unmap (outbuf, &omap); if (outsize < 0) { GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); ret = GST_FLOW_ERROR; goto done; } else if (outsize > enc->max_payload_size) { GST_WARNING_OBJECT (enc, "Encoded size %d is higher than max payload size (%d bytes)", outsize, enc->max_payload_size); ret = GST_FLOW_ERROR; goto done; } GST_DEBUG_OBJECT (enc, "Output packet is %u bytes", outsize); gst_buffer_set_size (outbuf, outsize); ret = gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (enc), outbuf, enc->frame_samples); done: if (bdata) gst_buffer_unmap (buf, &map); g_mutex_unlock (enc->property_lock); if (mdata) g_free (mdata); return ret; }
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::EncodeChunks(void* pcmBuf, int numSamples, bool lastChunk) { int numFrames = numSamples/PCM_SAMPLES_IN_FRAME; bool flush = false; for(int j = 0; j < numFrames; j++) { if(j == (numFrames -1) && (lastChunk == true)) { flush =true; } nb_samples=PCM_SAMPLES_IN_FRAME;//make it 160 id++; int size_segments,cur_frame_size; ////frame_size=160 for 8k cur_frame_size=frame_size; /*Encode current frame*/ //Stereo: each pcm samples will have 2 short samples(L/R interlaced) so the num of bytes for each frame will be double to mono nbBytes=opus_multistream_encode (st, (short*)pcmBuf + (j*PCM_SAMPLES_IN_FRAME*chan), cur_frame_size, m_outBuf, max_frame_bytes); if(nbBytes<0){ fprintf(stderr, "Encoding failed: %s. Aborting.\n", opus_strerror(nbBytes)); return; } nb_encoded+=cur_frame_size; enc_granulepos+=cur_frame_size*48000/coding_rate; total_bytes+=nbBytes; size_segments=(nbBytes+255)/255; peak_bytes=IMAX(nbBytes,peak_bytes); min_bytes=IMIN(nbBytes,min_bytes); //printf("TotalByes:%d\n", total_bytes); /*Flush early if adding this packet would make us end up with a continued page which we wouldn't have otherwise.*/ while((((size_segments<=255)&&(last_segments+size_segments>255))|| (enc_granulepos-last_granulepos>max_ogg_delay)) && ogg_stream_flush_fill(&os, &og,255*255)){ if(ogg_page_packets(&og)!=0) last_granulepos=ogg_page_granulepos(&og); last_segments-=og.header[26]; ret=oe_write_page(&og); if(ret!=og.header_len+og.body_len){ fprintf(stderr,"Error: failed writing data to output stream\n"); exit(1); } bytes_written+=ret; pages_out++; } /*The downside of early reading is if the input is an exact multiple of the frame_size you'll get an extra frame that needs to get cropped off. The downside of late reading is added delay. If your ogg_delay is 120ms or less we'll assume you want the low delay behavior.*/ // if(max_ogg_delay>5760){ // nb_samples = inopt.read_samples(inopt.readdata,input,frame_size); // total_samples+=nb_samples; // if(nb_samples==0)op.e_o_s=1; // } else nb_samples=-1; op.packet=(unsigned char *)m_outBuf; op.bytes=nbBytes; op.b_o_s=0; op.granulepos=enc_granulepos; if(flush == true){ /*We compute the final GP as ceil(len*48k/input_rate)+preskip. When a resampling decoder does the matching floor((len-preskip)*input_rate/48k) conversion, the resulting output length will exactly equal the original input length when 0<input_rate<=48000.*/ op.granulepos=((original_samples*48000+rate-1)/rate)+header.preskip; } op.packetno=2+id; ogg_stream_packetin(&os, &op); last_segments+=size_segments; /*If the stream is over or we're sure that the delayed flush will fire, go ahead and flush now to avoid adding delay.*/ while(((flush == true) || (enc_granulepos+(frame_size*48000/coding_rate)-last_granulepos>max_ogg_delay)|| (last_segments>=255))? ogg_stream_flush_fill(&os, &og,255*255): ogg_stream_pageout_fill(&os, &og,255*255)){ if(ogg_page_packets(&og)!=0)last_granulepos=ogg_page_granulepos(&og); last_segments-=og.header[26]; ret=oe_write_page(&og); if(ret!=og.header_len+og.body_len){ fprintf(stderr,"Error: failed writing data to output stream\n"); exit(1); } bytes_written+=ret; pages_out++; } } }