示例#1
0
文件: opus.c 项目: ifroz/rockbox
/* 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(&param);

        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;
}
示例#2
0
/* this is the codec entry point */
enum codec_status codec_main(void)
{
    ov_callbacks callbacks;
    OggVorbis_File vf;
    ogg_int32_t **pcm;

    int error;
    long n;
    int current_section;
    int previous_section;
    int eof;
    ogg_int64_t vf_offsets[2];
    ogg_int64_t vf_dataoffsets;
    ogg_uint32_t vf_serialnos;
    ogg_int64_t vf_pcmlengths[2];

    ci->configure(DSP_SET_SAMPLE_DEPTH, 24);

    if (codec_init()) {
        error = CODEC_ERROR;
        goto exit;
    }

    ogg_malloc_init();

#if defined(CPU_ARM) || defined(CPU_COLDFIRE) || defined(CPU_MIPS)
    if (setjmp(rb_jump_buf) != 0) {
        /* malloc failed; skip to next track */
        error = CODEC_ERROR;
        goto done;
    }
#endif

next_track:
    while (!*ci->taginfo_ready && !ci->stop_codec)
        ci->sleep(1);

    /* Create a decoder instance */
    callbacks.read_func = read_handler;
    callbacks.seek_func = initial_seek_handler;
    callbacks.tell_func = tell_handler;
    callbacks.close_func = close_handler;

    /* Open a non-seekable stream */
    error = ov_open_callbacks(ci, &vf, NULL, 0, callbacks);
    
    /* If the non-seekable open was successful, we need to supply the missing
     * data to make it seekable.  This is a hack, but it's reasonable since we
     * don't want to run the whole file through the buffer before we start
     * playing.  Using Tremor's seekable open routine would cause us to do
     * this, so we pretend not to be seekable at first.  Then we fill in the
     * missing fields of vf with 1) information in ci->id3, and 2) info
     * obtained by Tremor in the above ov_open call.
     *
     * Note that this assumes there is only ONE logical Vorbis bitstream in our
     * physical Ogg bitstream.  This is verified in metadata.c, well before we
     * get here.
     */
    if (!error) {
         ogg_free(vf.offsets);
         ogg_free(vf.dataoffsets);
         ogg_free(vf.serialnos);

         vf.offsets = vf_offsets;
         vf.dataoffsets = &vf_dataoffsets;
         vf.serialnos = &vf_serialnos;
         vf.pcmlengths = vf_pcmlengths;

         vf.offsets[0] = 0;
         vf.offsets[1] = ci->id3->filesize;
         vf.dataoffsets[0] = vf.offset;
         vf.pcmlengths[0] = 0;
         vf.pcmlengths[1] = ci->id3->samples;
         vf.serialnos[0] = vf.current_serialno;
         vf.callbacks.seek_func = seek_handler;
         vf.seekable = 1;
         vf.end = ci->id3->filesize;
         vf.ready_state = OPENED;
         vf.links = 1;
    } else {
         DEBUGF("Vorbis: ov_open failed: %d", error);
         error = CODEC_ERROR;
         goto done;
    }

    if (ci->id3->offset) {
        ci->advance_buffer(ci->id3->offset);
        ov_raw_seek(&vf, ci->id3->offset);
        ci->set_elapsed(ov_time_tell(&vf));
        ci->set_offset(ov_raw_tell(&vf));
    }

    previous_section = -1;
    eof = 0;
    while (!eof) {
        ci->yield();
        if (ci->stop_codec || ci->new_track)
            break;

        if (ci->seek_time) {
            if (ov_time_seek(&vf, ci->seek_time - 1)) {
                //ci->logf("ov_time_seek failed");
            }
            ci->seek_complete();
        }

        /* Read host-endian signed 24-bit PCM samples */
        n = ov_read_fixed(&vf, &pcm, 1024, &current_section);

        /* Change DSP and buffer settings for this bitstream */
        if (current_section != previous_section) {
            if (!vorbis_set_codec_parameters(&vf)) {
                error = CODEC_ERROR;
                goto done;
            } else {
                previous_section = current_section;
            }
        }

        if (n == 0) {
            eof = 1;
        } else if (n < 0) {
            DEBUGF("Vorbis: Error decoding frame\n");
        } else {
            ci->pcmbuf_insert(pcm[0], pcm[1], n);
            ci->set_offset(ov_raw_tell(&vf));
            ci->set_elapsed(ov_time_tell(&vf));
        }
    }
    error = CODEC_OK;
    
done:
#if 0 /* defined(SIMULATOR) */
    {
        size_t bufsize;
        void* buf = ci->codec_get_buffer(&bufsize);

        DEBUGF("Vorbis: Memory max: %u\n", get_max_size(buf));
    }
#endif

    if (ci->request_next_track()) {
        /* Clean things up for the next track */
        vf.dataoffsets = NULL;
        vf.offsets = NULL;
        vf.serialnos = NULL;
        vf.pcmlengths = NULL;
        ov_clear(&vf);
        goto next_track;
    }
        
exit:
    ogg_malloc_destroy();
    return error;
}