1 | /******************************************************************** |
2 | * * |
3 | * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * |
4 | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * |
5 | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * |
6 | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * |
7 | * * |
8 | * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * |
9 | * by the Xiph.Org Foundation https://xiph.org/ * |
10 | * * |
11 | ******************************************************************** |
12 | |
13 | function: bitrate tracking and management |
14 | |
15 | ********************************************************************/ |
16 | |
17 | #include <stdlib.h> |
18 | #include <string.h> |
19 | #include <math.h> |
20 | #include <ogg/ogg.h> |
21 | #include "vorbis/codec.h" |
22 | #include "codec_internal.h" |
23 | #include "os.h" |
24 | #include "misc.h" |
25 | #include "bitrate.h" |
26 | |
27 | /* compute bitrate tracking setup */ |
28 | void vorbis_bitrate_init(vorbis_info *vi,bitrate_manager_state *bm){ |
29 | codec_setup_info *ci=vi->codec_setup; |
30 | bitrate_manager_info *bi=&ci->bi; |
31 | |
32 | memset(bm,0,sizeof(*bm)); |
33 | |
34 | if(bi && (bi->reservoir_bits>0)){ |
35 | long ratesamples=vi->rate; |
36 | int halfsamples=ci->blocksizes[0]>>1; |
37 | |
38 | bm->short_per_long=ci->blocksizes[1]/ci->blocksizes[0]; |
39 | bm->managed=1; |
40 | |
41 | bm->avg_bitsper= rint(1.*bi->avg_rate*halfsamples/ratesamples); |
42 | bm->min_bitsper= rint(1.*bi->min_rate*halfsamples/ratesamples); |
43 | bm->max_bitsper= rint(1.*bi->max_rate*halfsamples/ratesamples); |
44 | |
45 | bm->avgfloat=PACKETBLOBS/2; |
46 | |
47 | /* not a necessary fix, but one that leads to a more balanced |
48 | typical initialization */ |
49 | { |
50 | long desired_fill=bi->reservoir_bits*bi->reservoir_bias; |
51 | bm->minmax_reservoir=desired_fill; |
52 | bm->avg_reservoir=desired_fill; |
53 | } |
54 | |
55 | } |
56 | } |
57 | |
58 | void vorbis_bitrate_clear(bitrate_manager_state *bm){ |
59 | memset(bm,0,sizeof(*bm)); |
60 | return; |
61 | } |
62 | |
63 | int vorbis_bitrate_managed(vorbis_block *vb){ |
64 | vorbis_dsp_state *vd=vb->vd; |
65 | private_state *b=vd->backend_state; |
66 | bitrate_manager_state *bm=&b->bms; |
67 | |
68 | if(bm && bm->managed)return(1); |
69 | return(0); |
70 | } |
71 | |
72 | /* finish taking in the block we just processed */ |
73 | int vorbis_bitrate_addblock(vorbis_block *vb){ |
74 | vorbis_block_internal *vbi=vb->internal; |
75 | vorbis_dsp_state *vd=vb->vd; |
76 | private_state *b=vd->backend_state; |
77 | bitrate_manager_state *bm=&b->bms; |
78 | vorbis_info *vi=vd->vi; |
79 | codec_setup_info *ci=vi->codec_setup; |
80 | bitrate_manager_info *bi=&ci->bi; |
81 | |
82 | int choice=rint(bm->avgfloat); |
83 | long this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
84 | long min_target_bits=(vb->W?bm->min_bitsper*bm->short_per_long:bm->min_bitsper); |
85 | long max_target_bits=(vb->W?bm->max_bitsper*bm->short_per_long:bm->max_bitsper); |
86 | int samples=ci->blocksizes[vb->W]>>1; |
87 | long desired_fill=bi->reservoir_bits*bi->reservoir_bias; |
88 | if(!bm->managed){ |
89 | /* not a bitrate managed stream, but for API simplicity, we'll |
90 | buffer the packet to keep the code path clean */ |
91 | |
92 | if(bm->vb)return(-1); /* one has been submitted without |
93 | being claimed */ |
94 | bm->vb=vb; |
95 | return(0); |
96 | } |
97 | |
98 | bm->vb=vb; |
99 | |
100 | /* look ahead for avg floater */ |
101 | if(bm->avg_bitsper>0){ |
102 | double slew=0.; |
103 | long avg_target_bits=(vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper); |
104 | double slewlimit= 15./bi->slew_damp; |
105 | |
106 | /* choosing a new floater: |
107 | if we're over target, we slew down |
108 | if we're under target, we slew up |
109 | |
110 | choose slew as follows: look through packetblobs of this frame |
111 | and set slew as the first in the appropriate direction that |
112 | gives us the slew we want. This may mean no slew if delta is |
113 | already favorable. |
114 | |
115 | Then limit slew to slew max */ |
116 | |
117 | if(bm->avg_reservoir+(this_bits-avg_target_bits)>desired_fill){ |
118 | while(choice>0 && this_bits>avg_target_bits && |
119 | bm->avg_reservoir+(this_bits-avg_target_bits)>desired_fill){ |
120 | choice--; |
121 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
122 | } |
123 | }else if(bm->avg_reservoir+(this_bits-avg_target_bits)<desired_fill){ |
124 | while(choice+1<PACKETBLOBS && this_bits<avg_target_bits && |
125 | bm->avg_reservoir+(this_bits-avg_target_bits)<desired_fill){ |
126 | choice++; |
127 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
128 | } |
129 | } |
130 | |
131 | slew=rint(choice-bm->avgfloat)/samples*vi->rate; |
132 | if(slew<-slewlimit)slew=-slewlimit; |
133 | if(slew>slewlimit)slew=slewlimit; |
134 | choice=rint(bm->avgfloat+= slew/vi->rate*samples); |
135 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
136 | } |
137 | |
138 | |
139 | |
140 | /* enforce min(if used) on the current floater (if used) */ |
141 | if(bm->min_bitsper>0){ |
142 | /* do we need to force the bitrate up? */ |
143 | if(this_bits<min_target_bits){ |
144 | while(bm->minmax_reservoir-(min_target_bits-this_bits)<0){ |
145 | choice++; |
146 | if(choice>=PACKETBLOBS)break; |
147 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
148 | } |
149 | } |
150 | } |
151 | |
152 | /* enforce max (if used) on the current floater (if used) */ |
153 | if(bm->max_bitsper>0){ |
154 | /* do we need to force the bitrate down? */ |
155 | if(this_bits>max_target_bits){ |
156 | while(bm->minmax_reservoir+(this_bits-max_target_bits)>bi->reservoir_bits){ |
157 | choice--; |
158 | if(choice<0)break; |
159 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
160 | } |
161 | } |
162 | } |
163 | |
164 | /* Choice of packetblobs now made based on floater, and min/max |
165 | requirements. Now boundary check extreme choices */ |
166 | |
167 | if(choice<0){ |
168 | /* choosing a smaller packetblob is insufficient to trim bitrate. |
169 | frame will need to be truncated */ |
170 | long maxsize=(max_target_bits+(bi->reservoir_bits-bm->minmax_reservoir))/8; |
171 | bm->choice=choice=0; |
172 | |
173 | if(oggpack_bytes(vbi->packetblob[choice])>maxsize){ |
174 | |
175 | oggpack_writetrunc(vbi->packetblob[choice],maxsize*8); |
176 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
177 | } |
178 | }else{ |
179 | long minsize=(min_target_bits-bm->minmax_reservoir+7)/8; |
180 | if(choice>=PACKETBLOBS) |
181 | choice=PACKETBLOBS-1; |
182 | |
183 | bm->choice=choice; |
184 | |
185 | /* prop up bitrate according to demand. pad this frame out with zeroes */ |
186 | minsize-=oggpack_bytes(vbi->packetblob[choice]); |
187 | while(minsize-->0)oggpack_write(vbi->packetblob[choice],0,8); |
188 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
189 | |
190 | } |
191 | |
192 | /* now we have the final packet and the final packet size. Update statistics */ |
193 | /* min and max reservoir */ |
194 | if(bm->min_bitsper>0 || bm->max_bitsper>0){ |
195 | |
196 | if(max_target_bits>0 && this_bits>max_target_bits){ |
197 | bm->minmax_reservoir+=(this_bits-max_target_bits); |
198 | }else if(min_target_bits>0 && this_bits<min_target_bits){ |
199 | bm->minmax_reservoir+=(this_bits-min_target_bits); |
200 | }else{ |
201 | /* inbetween; we want to take reservoir toward but not past desired_fill */ |
202 | if(bm->minmax_reservoir>desired_fill){ |
203 | if(max_target_bits>0){ /* logical bulletproofing against initialization state */ |
204 | bm->minmax_reservoir+=(this_bits-max_target_bits); |
205 | if(bm->minmax_reservoir<desired_fill)bm->minmax_reservoir=desired_fill; |
206 | }else{ |
207 | bm->minmax_reservoir=desired_fill; |
208 | } |
209 | }else{ |
210 | if(min_target_bits>0){ /* logical bulletproofing against initialization state */ |
211 | bm->minmax_reservoir+=(this_bits-min_target_bits); |
212 | if(bm->minmax_reservoir>desired_fill)bm->minmax_reservoir=desired_fill; |
213 | }else{ |
214 | bm->minmax_reservoir=desired_fill; |
215 | } |
216 | } |
217 | } |
218 | } |
219 | |
220 | /* avg reservoir */ |
221 | if(bm->avg_bitsper>0){ |
222 | long avg_target_bits=(vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper); |
223 | bm->avg_reservoir+=this_bits-avg_target_bits; |
224 | } |
225 | |
226 | return(0); |
227 | } |
228 | |
229 | int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd,ogg_packet *op){ |
230 | private_state *b=vd->backend_state; |
231 | bitrate_manager_state *bm=&b->bms; |
232 | vorbis_block *vb=bm->vb; |
233 | int choice=PACKETBLOBS/2; |
234 | if(!vb)return 0; |
235 | |
236 | if(op){ |
237 | vorbis_block_internal *vbi=vb->internal; |
238 | |
239 | if(vorbis_bitrate_managed(vb)) |
240 | choice=bm->choice; |
241 | |
242 | op->packet=oggpack_get_buffer(vbi->packetblob[choice]); |
243 | op->bytes=oggpack_bytes(vbi->packetblob[choice]); |
244 | op->b_o_s=0; |
245 | op->e_o_s=vb->eofflag; |
246 | op->granulepos=vb->granulepos; |
247 | op->packetno=vb->sequence; /* for sake of completeness */ |
248 | } |
249 | |
250 | bm->vb=0; |
251 | return(1); |
252 | } |
253 | |