/***************************************************************************** * ProcessInitialHeader: processes the inital Opus header packet. *****************************************************************************/ static int ProcessInitialHeader( decoder_t *p_dec, ogg_packet *p_oggpacket ) { int err; unsigned char new_stream_map[8]; decoder_sys_t *p_sys = p_dec->p_sys; OpusHeader *p_header = &p_sys->header; if( !opus_header_parse((unsigned char *)p_oggpacket->packet,p_oggpacket->bytes,p_header) ) { msg_Err( p_dec, "cannot read Opus header" ); return VLC_EGENERIC; } msg_Dbg( p_dec, "Opus audio with %d channels", p_header->channels); if((p_header->channels>2 && p_header->channel_mapping==0) || (p_header->channels>8 && p_header->channel_mapping==1) || p_header->channel_mapping>1) { msg_Err( p_dec, "Unsupported channel mapping" ); return VLC_EGENERIC; } /* Setup the format */ p_dec->fmt_out.audio.i_physical_channels = p_dec->fmt_out.audio.i_original_channels = pi_channels_maps[p_header->channels]; p_dec->fmt_out.audio.i_channels = p_header->channels; p_dec->fmt_out.audio.i_rate = 48000; if( p_header->channels>2 && p_header->channels<9 ) { static const uint32_t *pi_ch[6] = { pi_3channels_in, pi_4channels_in, pi_5channels_in, pi_6channels_in, pi_7channels_in, pi_8channels_in }; uint8_t pi_chan_table[AOUT_CHAN_MAX]; aout_CheckChannelReorder( pi_ch[p_header->channels-3], NULL, p_dec->fmt_out.audio.i_physical_channels, pi_chan_table ); for(int i=0;i<p_header->channels;i++) new_stream_map[pi_chan_table[i]]=p_header->stream_map[i]; } /* Opus decoder init */ p_sys->p_st = opus_multistream_decoder_create( 48000, p_header->channels, p_header->nb_streams, p_header->nb_coupled, p_header->channels>2?new_stream_map:p_header->stream_map, &err ); if( !p_sys->p_st || err!=OPUS_OK ) { msg_Err( p_dec, "decoder initialization failed" ); return VLC_EGENERIC; } #ifdef OPUS_SET_GAIN if( opus_multistream_decoder_ctl( p_sys->p_st,OPUS_SET_GAIN(p_header->gain) ) != OPUS_OK ) { msg_Err( p_dec, "OPUS_SET_GAIN failed" ); opus_multistream_decoder_destroy( p_sys->p_st ); return VLC_EGENERIC; } #endif date_Init( &p_sys->end_date, 48000, 1 ); return VLC_SUCCESS; }
/*Process an Opus header and setup the opus decoder based on it. It takes several pointers for header values which are needed elsewhere in the code.*/ static OpusMSDecoder *process_header(ogg_packet *op, opus_int32 *rate, int *mapping_family, int *channels, int *preskip, float *gain, float manual_gain, int *streams, int wav_format, int quiet) { int err; OpusMSDecoder *st; OpusHeader header; if (opus_header_parse(op->packet, op->bytes, &header)==0) { fprintf(stderr, "Cannot parse header\n"); return NULL; } *mapping_family = header.channel_mapping; *channels = header.channels; if(wav_format)adjust_wav_mapping(*mapping_family, *channels, header.stream_map); if(!*rate)*rate=header.input_sample_rate; /*If the rate is unspecified we decode to 48000*/ if(*rate==0)*rate=48000; if(*rate<8000||*rate>192000){ fprintf(stderr,"Warning: Crazy input_rate %d, decoding to 48000 instead.\n",*rate); *rate=48000; } *preskip = header.preskip; st = opus_multistream_decoder_create(48000, header.channels, header.nb_streams, header.nb_coupled, header.stream_map, &err); if(err != OPUS_OK){ fprintf(stderr, "Cannot create encoder: %s\n", opus_strerror(err)); return NULL; } if (!st) { fprintf (stderr, "Decoder initialization failed: %s\n", opus_strerror(err)); return NULL; } *streams=header.nb_streams; if(header.gain!=0 || manual_gain!=0) { /*Gain API added in a newer libopus version, if we don't have it we apply the gain ourselves. We also add in a user provided manual gain at the same time.*/ int gainadj = (int)(manual_gain*256.)+header.gain; #ifdef OPUS_SET_GAIN err=opus_multistream_decoder_ctl(st,OPUS_SET_GAIN(gainadj)); if(err==OPUS_UNIMPLEMENTED) { #endif *gain = pow(10., gainadj/5120.); #ifdef OPUS_SET_GAIN } else if (err!=OPUS_OK) { fprintf (stderr, "Error setting gain: %s\n", opus_strerror(err)); return NULL; } #endif } if (!quiet) { fprintf(stderr, "Decoding to %d Hz (%d channel%s)", *rate, *channels, *channels>1?"s":""); if(header.version!=1)fprintf(stderr, ", Header v%d",header.version); fprintf(stderr, "\n"); if (header.gain!=0)fprintf(stderr,"Playback gain: %f dB\n", header.gain/256.); if (manual_gain!=0)fprintf(stderr,"Manual gain: %f dB\n", manual_gain); } return st; }
/* this is called for each file to process */ enum codec_status codec_run(void) { int error = CODEC_ERROR; intptr_t param; ogg_sync_state oy; ogg_page og; ogg_packet op; ogg_stream_state os; int64_t page_granule = 0; int stream_init = 0; int sample_rate = 48000; OpusDecoder *st = NULL; OpusHeader header; int ret; unsigned long strtoffset = ci->id3->offset; int skip = 0; int64_t seek_target; uint64_t granule_pos; ogg_malloc_init(); global_stack = 0; #if defined(CPU_COLDFIRE) /* EMAC rounding is disabled because of MULT16_32_Q15, which will be inaccurate with rounding in its current incarnation */ coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE); #endif /* pre-init the ogg_sync_state buffer, so it won't need many reallocs */ ogg_sync_init(&oy); oy.storage = 64*1024; oy.data = _ogg_malloc(oy.storage); /* allocate output buffer */ uint16_t *output = (uint16_t*) _ogg_malloc(MAX_FRAME_SIZE*sizeof(uint16_t)); ci->seek_buffer(0); ci->set_elapsed(0); while (1) { enum codec_command_action action = ci->get_command(¶m); if (action == CODEC_ACTION_HALT) break; if (action == CODEC_ACTION_SEEK_TIME) { if (st != NULL) { /* calculate granule to seek to (including seek rewind) */ seek_target = (48LL * param) + header.preskip; skip = MIN(seek_target, SEEK_REWIND); seek_target -= skip; LOGF("Opus seek page:%lld,%lld,%ld\n", seek_target, page_granule, (long)param); speex_seek_page_granule(seek_target, page_granule, &oy, &os); } ci->set_elapsed(param); ci->seek_complete(); } /*Get the ogg buffer for writing*/ if (get_more_data(&oy) < 1) { goto done; } /* Loop for all complete pages we got (most likely only one) */ while (ogg_sync_pageout(&oy, &og) == 1) { if (stream_init == 0) { ogg_stream_init(&os, ogg_page_serialno(&og)); stream_init = 1; } /* Add page to the bitstream */ ogg_stream_pagein(&os, &og); page_granule = ogg_page_granulepos(&og); granule_pos = page_granule; /* Do this to avoid allocating space for huge comment packets (embedded Album Art) */ if(os.packetno == 1 && ogg_stream_packetpeek(&os, &op) != 1){ ogg_sync_reset(&oy); } while ((ogg_stream_packetout(&os, &op) == 1) && !op.e_o_s) { if (op.packetno == 0){ /* identification header */ if (opus_header_parse(op.packet, op.bytes, &header) == 0) { LOGF("Could not parse header"); goto done; } skip = header.preskip; st = opus_decoder_create(sample_rate, header.channels, &ret); if (ret != OPUS_OK) { LOGF("opus_decoder_create failed %d", ret); goto done; } LOGF("Decoder inited"); codec_set_replaygain(ci->id3); opus_decoder_ctl(st, OPUS_SET_GAIN(header.gain)); ci->configure(DSP_SET_FREQUENCY, sample_rate); ci->configure(DSP_SET_SAMPLE_DEPTH, 16); ci->configure(DSP_SET_STEREO_MODE, (header.channels == 2) ? STEREO_INTERLEAVED : STEREO_MONO); } else if (op.packetno == 1) { /* Comment header */ } else { if (strtoffset) { ci->seek_buffer(strtoffset); ogg_sync_reset(&oy); strtoffset = 0; break;//next page } /* report progress */ ci->set_elapsed((granule_pos - header.preskip) / 48); /* Decode audio packets */ ret = opus_decode(st, op.packet, op.bytes, output, MAX_FRAME_SIZE, 0); if (ret > 0) { if (skip > 0) { if (ret <= skip) { /* entire output buffer is skipped */ skip -= ret; ret = 0; } else { /* part of output buffer is played */ ret -= skip; ci->pcmbuf_insert(&output[skip * header.channels], NULL, ret); skip = 0; } } else { /* entire buffer is played */ ci->pcmbuf_insert(output, NULL, ret); } granule_pos += ret; } else { if (ret < 0) { LOGF("opus_decode failed %d", ret); goto done; } break; } } } } } LOGF("Returned OK"); error = CODEC_OK; done: ogg_malloc_destroy(); return error; }
krad_opus_t *krad_opus_decoder_create (unsigned char *header_data, int header_length, float output_sample_rate) { int c; krad_opus_t *krad_opus = calloc (1, sizeof(krad_opus_t)); krad_opus->output_sample_rate = output_sample_rate; krad_opus->opus_header = calloc (1, sizeof(OpusHeader)); if (opus_header_parse (header_data, header_length, krad_opus->opus_header) != 1) { failfast ("krad_opus_decoder_create problem reading opus header"); } // oops //krad_opus->input_sample_rate = krad_opus->opus_header->input_sample_rate; krad_opus->channels = krad_opus->opus_header->channels; krad_opus->interleaved_samples = malloc(16 * 8192); 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->read_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_decoder_create src resampler error: %s", src_strerror (krad_opus->src_error[c])); } krad_opus->src_data[c].src_ratio = output_sample_rate / 48000; printk ("krad_opus_decoder_create src resampler ratio is: %f", krad_opus->src_data[c].src_ratio); } krad_opus->streams = krad_opus->opus_header->nb_streams; krad_opus->coupled_streams = krad_opus->opus_header->nb_coupled; memcpy (krad_opus->mapping, krad_opus->opus_header->stream_map, 256); printk ("krad_opus_decoder_create channels %d streams %d coupled %d", krad_opus->channels, krad_opus->streams, krad_opus->coupled_streams ); krad_opus->decoder = opus_multistream_decoder_create (48000, krad_opus->opus_header->channels, krad_opus->streams, krad_opus->coupled_streams, krad_opus->mapping, &krad_opus->opus_decoder_error); if (krad_opus->opus_decoder_error != OPUS_OK) { failfast ("Cannot create decoder: %s", opus_strerror (krad_opus->opus_decoder_error)); } return krad_opus; }