/src/vorbis/lib/bitrate.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | void vorbis_bitrate_init(vorbis_info *vi,bitrate_manager_state *bm){ |
29 | 0 | codec_setup_info *ci=vi->codec_setup; |
30 | 0 | bitrate_manager_info *bi=&ci->bi; |
31 | |
|
32 | 0 | memset(bm,0,sizeof(*bm)); |
33 | |
|
34 | 0 | if(bi && (bi->reservoir_bits>0)){ |
35 | 0 | long ratesamples=vi->rate; |
36 | 0 | int halfsamples=ci->blocksizes[0]>>1; |
37 | |
|
38 | 0 | bm->short_per_long=ci->blocksizes[1]/ci->blocksizes[0]; |
39 | 0 | bm->managed=1; |
40 | |
|
41 | 0 | bm->avg_bitsper= rint(1.*bi->avg_rate*halfsamples/ratesamples); |
42 | 0 | bm->min_bitsper= rint(1.*bi->min_rate*halfsamples/ratesamples); |
43 | 0 | bm->max_bitsper= rint(1.*bi->max_rate*halfsamples/ratesamples); |
44 | |
|
45 | 0 | bm->avgfloat=PACKETBLOBS/2; |
46 | | |
47 | | /* not a necessary fix, but one that leads to a more balanced |
48 | | typical initialization */ |
49 | 0 | { |
50 | 0 | long desired_fill=bi->reservoir_bits*bi->reservoir_bias; |
51 | 0 | bm->minmax_reservoir=desired_fill; |
52 | 0 | bm->avg_reservoir=desired_fill; |
53 | 0 | } |
54 | |
|
55 | 0 | } |
56 | 0 | } |
57 | | |
58 | 0 | void vorbis_bitrate_clear(bitrate_manager_state *bm){ |
59 | 0 | memset(bm,0,sizeof(*bm)); |
60 | 0 | return; |
61 | 0 | } |
62 | | |
63 | 0 | int vorbis_bitrate_managed(vorbis_block *vb){ |
64 | 0 | vorbis_dsp_state *vd=vb->vd; |
65 | 0 | private_state *b=vd->backend_state; |
66 | 0 | bitrate_manager_state *bm=&b->bms; |
67 | |
|
68 | 0 | if(bm && bm->managed)return(1); |
69 | 0 | return(0); |
70 | 0 | } |
71 | | |
72 | | /* finish taking in the block we just processed */ |
73 | 0 | int vorbis_bitrate_addblock(vorbis_block *vb){ |
74 | 0 | vorbis_block_internal *vbi=vb->internal; |
75 | 0 | vorbis_dsp_state *vd=vb->vd; |
76 | 0 | private_state *b=vd->backend_state; |
77 | 0 | bitrate_manager_state *bm=&b->bms; |
78 | 0 | vorbis_info *vi=vd->vi; |
79 | 0 | codec_setup_info *ci=vi->codec_setup; |
80 | 0 | bitrate_manager_info *bi=&ci->bi; |
81 | |
|
82 | 0 | int choice=rint(bm->avgfloat); |
83 | 0 | long this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
84 | 0 | long min_target_bits=(vb->W?bm->min_bitsper*bm->short_per_long:bm->min_bitsper); |
85 | 0 | long max_target_bits=(vb->W?bm->max_bitsper*bm->short_per_long:bm->max_bitsper); |
86 | 0 | int samples=ci->blocksizes[vb->W]>>1; |
87 | 0 | long desired_fill=bi->reservoir_bits*bi->reservoir_bias; |
88 | 0 | 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 | 0 | if(bm->vb)return(-1); /* one has been submitted without |
93 | | being claimed */ |
94 | 0 | bm->vb=vb; |
95 | 0 | return(0); |
96 | 0 | } |
97 | | |
98 | 0 | bm->vb=vb; |
99 | | |
100 | | /* look ahead for avg floater */ |
101 | 0 | if(bm->avg_bitsper>0){ |
102 | 0 | double slew=0.; |
103 | 0 | long avg_target_bits=(vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper); |
104 | 0 | 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 | 0 | if(bm->avg_reservoir+(this_bits-avg_target_bits)>desired_fill){ |
118 | 0 | while(choice>0 && this_bits>avg_target_bits && |
119 | 0 | bm->avg_reservoir+(this_bits-avg_target_bits)>desired_fill){ |
120 | 0 | choice--; |
121 | 0 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
122 | 0 | } |
123 | 0 | }else if(bm->avg_reservoir+(this_bits-avg_target_bits)<desired_fill){ |
124 | 0 | while(choice+1<PACKETBLOBS && this_bits<avg_target_bits && |
125 | 0 | bm->avg_reservoir+(this_bits-avg_target_bits)<desired_fill){ |
126 | 0 | choice++; |
127 | 0 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
128 | 0 | } |
129 | 0 | } |
130 | |
|
131 | 0 | slew=rint(choice-bm->avgfloat)/samples*vi->rate; |
132 | 0 | if(slew<-slewlimit)slew=-slewlimit; |
133 | 0 | if(slew>slewlimit)slew=slewlimit; |
134 | 0 | choice=rint(bm->avgfloat+= slew/vi->rate*samples); |
135 | 0 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
136 | 0 | } |
137 | | |
138 | | |
139 | | |
140 | | /* enforce min(if used) on the current floater (if used) */ |
141 | 0 | if(bm->min_bitsper>0){ |
142 | | /* do we need to force the bitrate up? */ |
143 | 0 | if(this_bits<min_target_bits){ |
144 | 0 | while(bm->minmax_reservoir-(min_target_bits-this_bits)<0){ |
145 | 0 | choice++; |
146 | 0 | if(choice>=PACKETBLOBS)break; |
147 | 0 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
148 | 0 | } |
149 | 0 | } |
150 | 0 | } |
151 | | |
152 | | /* enforce max (if used) on the current floater (if used) */ |
153 | 0 | if(bm->max_bitsper>0){ |
154 | | /* do we need to force the bitrate down? */ |
155 | 0 | if(this_bits>max_target_bits){ |
156 | 0 | while(bm->minmax_reservoir+(this_bits-max_target_bits)>bi->reservoir_bits){ |
157 | 0 | choice--; |
158 | 0 | if(choice<0)break; |
159 | 0 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
160 | 0 | } |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | | /* Choice of packetblobs now made based on floater, and min/max |
165 | | requirements. Now boundary check extreme choices */ |
166 | |
|
167 | 0 | if(choice<0){ |
168 | | /* choosing a smaller packetblob is insufficient to trim bitrate. |
169 | | frame will need to be truncated */ |
170 | 0 | long maxsize=(max_target_bits+(bi->reservoir_bits-bm->minmax_reservoir))/8; |
171 | 0 | bm->choice=choice=0; |
172 | |
|
173 | 0 | if(oggpack_bytes(vbi->packetblob[choice])>maxsize){ |
174 | |
|
175 | 0 | oggpack_writetrunc(vbi->packetblob[choice],maxsize*8); |
176 | 0 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
177 | 0 | } |
178 | 0 | }else{ |
179 | 0 | long minsize=(min_target_bits-bm->minmax_reservoir+7)/8; |
180 | 0 | if(choice>=PACKETBLOBS) |
181 | 0 | choice=PACKETBLOBS-1; |
182 | |
|
183 | 0 | bm->choice=choice; |
184 | | |
185 | | /* prop up bitrate according to demand. pad this frame out with zeroes */ |
186 | 0 | minsize-=oggpack_bytes(vbi->packetblob[choice]); |
187 | 0 | while(minsize-->0)oggpack_write(vbi->packetblob[choice],0,8); |
188 | 0 | this_bits=oggpack_bytes(vbi->packetblob[choice])*8; |
189 | |
|
190 | 0 | } |
191 | | |
192 | | /* now we have the final packet and the final packet size. Update statistics */ |
193 | | /* min and max reservoir */ |
194 | 0 | if(bm->min_bitsper>0 || bm->max_bitsper>0){ |
195 | |
|
196 | 0 | if(max_target_bits>0 && this_bits>max_target_bits){ |
197 | 0 | bm->minmax_reservoir+=(this_bits-max_target_bits); |
198 | 0 | }else if(min_target_bits>0 && this_bits<min_target_bits){ |
199 | 0 | bm->minmax_reservoir+=(this_bits-min_target_bits); |
200 | 0 | }else{ |
201 | | /* inbetween; we want to take reservoir toward but not past desired_fill */ |
202 | 0 | if(bm->minmax_reservoir>desired_fill){ |
203 | 0 | if(max_target_bits>0){ /* logical bulletproofing against initialization state */ |
204 | 0 | bm->minmax_reservoir+=(this_bits-max_target_bits); |
205 | 0 | if(bm->minmax_reservoir<desired_fill)bm->minmax_reservoir=desired_fill; |
206 | 0 | }else{ |
207 | 0 | bm->minmax_reservoir=desired_fill; |
208 | 0 | } |
209 | 0 | }else{ |
210 | 0 | if(min_target_bits>0){ /* logical bulletproofing against initialization state */ |
211 | 0 | bm->minmax_reservoir+=(this_bits-min_target_bits); |
212 | 0 | if(bm->minmax_reservoir>desired_fill)bm->minmax_reservoir=desired_fill; |
213 | 0 | }else{ |
214 | 0 | bm->minmax_reservoir=desired_fill; |
215 | 0 | } |
216 | 0 | } |
217 | 0 | } |
218 | 0 | } |
219 | | |
220 | | /* avg reservoir */ |
221 | 0 | if(bm->avg_bitsper>0){ |
222 | 0 | long avg_target_bits=(vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper); |
223 | 0 | bm->avg_reservoir+=this_bits-avg_target_bits; |
224 | 0 | } |
225 | |
|
226 | 0 | return(0); |
227 | 0 | } |
228 | | |
229 | 0 | int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd,ogg_packet *op){ |
230 | 0 | private_state *b=vd->backend_state; |
231 | 0 | bitrate_manager_state *bm=&b->bms; |
232 | 0 | vorbis_block *vb=bm->vb; |
233 | 0 | int choice=PACKETBLOBS/2; |
234 | 0 | if(!vb)return 0; |
235 | | |
236 | 0 | if(op){ |
237 | 0 | vorbis_block_internal *vbi=vb->internal; |
238 | |
|
239 | 0 | if(vorbis_bitrate_managed(vb)) |
240 | 0 | choice=bm->choice; |
241 | |
|
242 | 0 | op->packet=oggpack_get_buffer(vbi->packetblob[choice]); |
243 | 0 | op->bytes=oggpack_bytes(vbi->packetblob[choice]); |
244 | 0 | op->b_o_s=0; |
245 | 0 | op->e_o_s=vb->eofflag; |
246 | 0 | op->granulepos=vb->granulepos; |
247 | 0 | op->packetno=vb->sequence; /* for sake of completeness */ |
248 | 0 | } |
249 | |
|
250 | 0 | bm->vb=0; |
251 | 0 | return(1); |
252 | 0 | } |