--- a/include/sndfile.h +++ b/include/sndfile.h @@ -229,6 +229,9 @@ SFC_GET_BITRATE_MODE = 0x1304, SFC_SET_BITRATE_MODE = 0x1305, + /* WebEngine extension: int target bits/s, native Vorbis managed encoding. */ + SFC_WEB_SET_VORBIS_BITRATE = 0x7B01, + /* Cart Chunk support */ SFC_SET_CART_INFO = 0x1400, SFC_GET_CART_INFO = 0x1401, --- a/src/ogg_vorbis.c +++ b/src/ogg_vorbis.c @@ -137,6 +137,9 @@ vorbis_block vblock ; /* Encoding quality in range [0.0, 1.0]. */ double quality ; + /* WebEngine: zero keeps upstream VBR quality mode. */ + int nominal_bitrate ; + int header_ready ; /* Offset of the first samples' granule position. */ uint64_t pcm_start ; /* Last valid samples' granule position. */ @@ -313,20 +316,22 @@ VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; int k, ret ; + if (vdata->header_ready) + return 0 ; + vorbis_info_init (&vdata->vinfo) ; - /* The style of encoding should be selectable here, VBR quality mode. */ - ret = vorbis_encode_init_vbr (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, vdata->quality) ; - -#if 0 - ret = vorbis_encode_init (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, -1, 128000, -1) ; /* average bitrate mode */ - ret = ( vorbis_encode_setup_managed (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, -1, 128000, -1) - || vorbis_encode_ctl (&vdata->vinfo, OV_ECTL_RATEMANAGE_AVG, NULL) - || vorbis_encode_setup_init (&vdata->vinfo) - ) ; -#endif + /* Select libvorbis' original managed average bitrate encoder when requested. + ** No bitrate-to-quality approximation: the native codec owns rate control. */ + if (vdata->nominal_bitrate > 0) + ret = vorbis_encode_init (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, + -1, vdata->nominal_bitrate, -1) ; + else + ret = vorbis_encode_init_vbr (&vdata->vinfo, psf->sf.channels, psf->sf.samplerate, vdata->quality) ; if (ret) + { vorbis_info_clear (&vdata->vinfo) ; return SFE_BAD_OPEN_FORMAT ; + } ; vdata->gp = 0 ; @@ -395,6 +400,7 @@ } ; } + vdata->header_ready = SF_TRUE ; return 0 ; } /* vorbis_write_header */ @@ -402,6 +408,7 @@ vorbis_close (SF_PRIVATE *psf) { OGG_PRIVATE* odata = psf->container_data ; VORBIS_PRIVATE *vdata = psf->codec_data ; + int error = 0 ; if (odata == NULL || vdata == NULL) return 0 ; @@ -411,8 +418,10 @@ if (psf->file.mode == SFM_WRITE) { - if (psf->write_current <= 0) - vorbis_write_header (psf, 0) ; + /* A rejected rate/rate-channel combination must never drain an + ** uninitialized encoder or leak its native allocations on close. */ + if (! vdata->header_ready && (error = vorbis_write_header (psf, 0))) + goto cleanup ; vorbis_analysis_wrote (&vdata->vdsp, 0) ; while (vorbis_analysis_blockout (&vdata->vdsp, &vdata->vblock) == 1) @@ -441,6 +450,7 @@ } } +cleanup: /* ogg_page and ogg_packet structs always point to storage in libvorbis. They are never freed or manipulated directly */ @@ -449,7 +459,7 @@ vorbis_comment_clear (&vdata->vcomment) ; vorbis_info_clear (&vdata->vinfo) ; - return 0 ; + return error ; } /* vorbis_close */ int @@ -515,12 +525,36 @@ VORBIS_PRIVATE *vdata = (VORBIS_PRIVATE *) psf->codec_data ; switch (command) - { case SFC_SET_COMPRESSION_LEVEL : + { case SFC_WEB_SET_VORBIS_BITRATE : + { vorbis_info candidate ; + int bitrate, result ; + if (psf->file.mode != SFM_WRITE || psf->have_written || vdata->header_ready || + data == NULL || datasize != sizeof (int)) + return SF_FALSE ; + bitrate = *((int *) data) ; + if (bitrate <= 0) + return SF_FALSE ; + + /* Ask the same encoder to accept the complete rate/channel tuple + ** before retaining it. A failed command leaves settings unchanged. */ + vorbis_info_init (&candidate) ; + result = vorbis_encode_init (&candidate, psf->sf.channels, psf->sf.samplerate, + -1, bitrate, -1) ; + vorbis_info_clear (&candidate) ; + if (result) + return SF_FALSE ; + vdata->nominal_bitrate = bitrate ; + return SF_TRUE ; + } ; + + case SFC_SET_COMPRESSION_LEVEL : if (data == NULL || datasize != sizeof (double)) return SF_FALSE ; - if (psf->have_written) + if (psf->file.mode != SFM_WRITE || psf->have_written || vdata->header_ready) return SF_FALSE ; + + vdata->nominal_bitrate = 0 ; vdata->quality = 1.0 - *((double *) data) ;