• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 Google Inc.
2 //
3 // This code is licensed under the same terms as WebM:
4 //  Software License Agreement:  http://www.webmproject.org/license/software/
5 //  Additional IP Rights Grant:  http://www.webmproject.org/license/additional/
6 // -----------------------------------------------------------------------------
7 //
8 // Header syntax writing
9 //
10 // Author: Skal (pascal.massimino@gmail.com)
11 
12 #include <assert.h>
13 #include <math.h>
14 
15 #include "vp8enci.h"
16 
17 #if defined(__cplusplus) || defined(c_plusplus)
18 extern "C" {
19 #endif
20 
21 #define KSIGNATURE 0x9d012a
22 #define KHEADER_SIZE 10
23 #define KRIFF_SIZE 20
24 #define KSIZE_OFFSET (KRIFF_SIZE - 8)
25 
26 #define MAX_PARTITION0_SIZE (1 << 19)   // max size of mode partition
27 #define MAX_PARTITION_SIZE  (1 << 24)   // max size for token partition
28 
29 //-----------------------------------------------------------------------------
30 // Writers for header's various pieces (in order of appearance)
31 
32 // Main keyframe header
33 
PutLE32(uint8_t * const data,uint32_t val)34 static void PutLE32(uint8_t* const data, uint32_t val) {
35   data[0] = (val >>  0) & 0xff;
36   data[1] = (val >>  8) & 0xff;
37   data[2] = (val >> 16) & 0xff;
38   data[3] = (val >> 24) & 0xff;
39 }
40 
PutHeader(int profile,size_t size0,size_t total_size,WebPPicture * const pic)41 static int PutHeader(int profile, size_t size0, size_t total_size,
42                      WebPPicture* const pic) {
43   uint8_t buf[KHEADER_SIZE];
44   uint8_t RIFF[KRIFF_SIZE] = {
45     'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P', 'V', 'P', '8', ' '
46   };
47   uint32_t bits;
48 
49   if (size0 >= MAX_PARTITION0_SIZE) {  // partition #0 is too big to fit
50     return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION0_OVERFLOW);
51   }
52 
53   PutLE32(RIFF + 4, total_size + KSIZE_OFFSET);
54   PutLE32(RIFF + 16, total_size);
55   if (!pic->writer(RIFF, sizeof(RIFF), pic)) {
56     return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
57   }
58 
59   bits = 0               // keyframe (1b)
60        | (profile << 1)  // profile (3b)
61        | (1 << 4)        // visible (1b)
62        | (size0 << 5);   // partition length (19b)
63   buf[0] = bits & 0xff;
64   buf[1] = (bits >> 8) & 0xff;
65   buf[2] = (bits >> 16) & 0xff;
66   // signature
67   buf[3] = (KSIGNATURE >> 16) & 0xff;
68   buf[4] = (KSIGNATURE >> 8) & 0xff;
69   buf[5] = (KSIGNATURE >> 0) & 0xff;
70   // dimensions
71   buf[6] = pic->width & 0xff;
72   buf[7] = pic->width >> 8;
73   buf[8] = pic->height & 0xff;
74   buf[9] = pic->height >> 8;
75 
76   return pic->writer(buf, sizeof(buf), pic);
77 }
78 
79 // Segmentation header
PutSegmentHeader(VP8BitWriter * const bw,const VP8Encoder * const enc)80 static void PutSegmentHeader(VP8BitWriter* const bw,
81                              const VP8Encoder* const enc) {
82   const VP8SegmentHeader* const hdr = &enc->segment_hdr_;
83   const VP8Proba* const proba = &enc->proba_;
84   if (VP8PutBitUniform(bw, (hdr->num_segments_ > 1))) {
85     // We always 'update' the quant and filter strength values
86     const int update_data = 1;
87     int s;
88     VP8PutBitUniform(bw, hdr->update_map_);
89     if (VP8PutBitUniform(bw, update_data)) {
90       // we always use absolute values, not relative ones
91       VP8PutBitUniform(bw, 1);   // (segment_feature_mode = 1. Paragraph 9.3.)
92       for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
93         VP8PutSignedValue(bw, enc->dqm_[s].quant_, 7);
94       }
95       for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
96         VP8PutSignedValue(bw, enc->dqm_[s].fstrength_, 6);
97       }
98     }
99     if (hdr->update_map_) {
100       for (s = 0; s < 3; ++s) {
101         if (VP8PutBitUniform(bw, (proba->segments_[s] != 255u))) {
102           VP8PutValue(bw, proba->segments_[s], 8);
103         }
104       }
105     }
106   }
107 }
108 
109 // Filtering parameters header
PutFilterHeader(VP8BitWriter * const bw,const VP8FilterHeader * const hdr)110 static void PutFilterHeader(VP8BitWriter* const bw,
111                             const VP8FilterHeader* const hdr) {
112   const int use_lf_delta = (hdr->i4x4_lf_delta_ != 0);
113   VP8PutBitUniform(bw, hdr->simple_);
114   VP8PutValue(bw, hdr->level_, 6);
115   VP8PutValue(bw, hdr->sharpness_, 3);
116   if (VP8PutBitUniform(bw, use_lf_delta)) {
117     // '0' is the default value for i4x4_lf_delta_ at frame #0.
118     const int need_update = (hdr->i4x4_lf_delta_ != 0);
119     if (VP8PutBitUniform(bw, need_update)) {
120       // we don't use ref_lf_delta => emit four 0 bits
121       VP8PutValue(bw, 0, 4);
122       // we use mode_lf_delta for i4x4
123       VP8PutSignedValue(bw, hdr->i4x4_lf_delta_, 6);
124       VP8PutValue(bw, 0, 3);    // all others unused
125     }
126   }
127 }
128 
129 // Nominal quantization parameters
PutQuant(VP8BitWriter * const bw,const VP8Encoder * const enc)130 static void PutQuant(VP8BitWriter* const bw,
131                      const VP8Encoder* const enc) {
132   VP8PutValue(bw, enc->base_quant_, 7);
133   VP8PutSignedValue(bw, enc->dq_y1_dc_, 4);
134   VP8PutSignedValue(bw, enc->dq_y2_dc_, 4);
135   VP8PutSignedValue(bw, enc->dq_y2_ac_, 4);
136   VP8PutSignedValue(bw, enc->dq_uv_dc_, 4);
137   VP8PutSignedValue(bw, enc->dq_uv_ac_, 4);
138 }
139 
140 // Partition sizes
EmitPartitionsSize(const VP8Encoder * const enc,WebPPicture * const pic)141 static int EmitPartitionsSize(const VP8Encoder* const enc,
142                               WebPPicture* const pic) {
143   uint8_t buf[3 * (MAX_NUM_PARTITIONS - 1)];
144   int p;
145   for (p = 0; p < enc->num_parts_ - 1; ++p) {
146     const size_t part_size = VP8BitWriterSize(enc->parts_ + p);
147     if (part_size >= MAX_PARTITION_SIZE) {
148       return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW);
149     }
150     buf[3 * p + 0] = (part_size >>  0) & 0xff;
151     buf[3 * p + 1] = (part_size >>  8) & 0xff;
152     buf[3 * p + 2] = (part_size >> 16) & 0xff;
153   }
154   return p ? pic->writer(buf, 3 * p, pic) : 1;
155 }
156 
157 //-----------------------------------------------------------------------------
158 
159 #ifdef WEBP_EXPERIMENTAL_FEATURES
160 
161 #define KTRAILER_SIZE 8
162 
PutLE24(uint8_t * buf,size_t value)163 static void PutLE24(uint8_t* buf, size_t value) {
164   buf[0] = (value >>  0) & 0xff;
165   buf[1] = (value >>  8) & 0xff;
166   buf[2] = (value >> 16) & 0xff;
167 }
168 
WriteExtensions(VP8Encoder * const enc)169 static int WriteExtensions(VP8Encoder* const enc) {
170   uint8_t buffer[KTRAILER_SIZE];
171   VP8BitWriter* const bw = &enc->bw_;
172   WebPPicture* const pic = enc->pic_;
173 
174   // Layer (bytes 0..3)
175   PutLE24(buffer + 0, enc->layer_data_size_);
176   buffer[3] = enc->pic_->colorspace & WEBP_CSP_UV_MASK;
177   if (enc->layer_data_size_ > 0) {
178     assert(enc->use_layer_);
179     // append layer data to last partition
180     if (!VP8BitWriterAppend(&enc->parts_[enc->num_parts_ - 1],
181                             enc->layer_data_, enc->layer_data_size_)) {
182       return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
183     }
184   }
185   // Alpha (bytes 4..6)
186   PutLE24(buffer + 4, enc->alpha_data_size_);
187   if (enc->alpha_data_size_ > 0) {
188     assert(enc->has_alpha_);
189     if (!VP8BitWriterAppend(bw, enc->alpha_data_, enc->alpha_data_size_)) {
190       return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
191     }
192   }
193 
194   buffer[KTRAILER_SIZE - 1] = 0x01;  // marker
195   if (!VP8BitWriterAppend(bw, buffer, KTRAILER_SIZE)) {
196     return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
197   }
198   return 1;
199 }
200 
201 #endif    /* WEBP_EXPERIMENTAL_FEATURES */
202 
203 //-----------------------------------------------------------------------------
204 
GeneratePartition0(VP8Encoder * const enc)205 static size_t GeneratePartition0(VP8Encoder* const enc) {
206   VP8BitWriter* const bw = &enc->bw_;
207   const int mb_size = enc->mb_w_ * enc->mb_h_;
208   uint64_t pos1, pos2, pos3;
209 #ifdef WEBP_EXPERIMENTAL_FEATURES
210   const int need_extensions = enc->has_alpha_ || enc->use_layer_;
211 #endif
212 
213   pos1 = VP8BitWriterPos(bw);
214   VP8BitWriterInit(bw, mb_size * 7 / 8);        // ~7 bits per macroblock
215 #ifdef WEBP_EXPERIMENTAL_FEATURES
216   VP8PutBitUniform(bw, need_extensions);   // extensions
217 #else
218   VP8PutBitUniform(bw, 0);   // colorspace
219 #endif
220   VP8PutBitUniform(bw, 0);   // clamp type
221 
222   PutSegmentHeader(bw, enc);
223   PutFilterHeader(bw, &enc->filter_hdr_);
224   VP8PutValue(bw, enc->config_->partitions, 2);
225   PutQuant(bw, enc);
226   VP8PutBitUniform(bw, 0);   // no proba update
227   VP8WriteProbas(bw, &enc->proba_);
228   pos2 = VP8BitWriterPos(bw);
229   VP8CodeIntraModes(enc);
230   VP8BitWriterFinish(bw);
231 
232 #ifdef WEBP_EXPERIMENTAL_FEATURES
233   if (need_extensions && !WriteExtensions(enc)) {
234     return 0;
235   }
236 #endif
237 
238   pos3 = VP8BitWriterPos(bw);
239 
240   if (enc->pic_->stats) {
241     enc->pic_->stats->header_bytes[0] = (int)((pos2 - pos1 + 7) >> 3);
242     enc->pic_->stats->header_bytes[1] = (int)((pos3 - pos2 + 7) >> 3);
243     enc->pic_->stats->alpha_data_size = enc->alpha_data_size_;
244     enc->pic_->stats->layer_data_size = enc->layer_data_size_;
245   }
246   return !bw->error_;
247 }
248 
VP8EncWrite(VP8Encoder * const enc)249 int VP8EncWrite(VP8Encoder* const enc) {
250   WebPPicture* const pic = enc->pic_;
251   VP8BitWriter* const bw = &enc->bw_;
252   int ok = 0;
253   size_t coded_size, pad;
254   int p;
255 
256   // Partition #0 with header and partition sizes
257   ok = GeneratePartition0(enc);
258 
259   // Compute total size (for the RIFF header)
260   coded_size = KHEADER_SIZE + VP8BitWriterSize(bw) + 3 * (enc->num_parts_ - 1);
261   for (p = 0; p < enc->num_parts_; ++p) {
262     coded_size += VP8BitWriterSize(enc->parts_ + p);
263   }
264   pad = coded_size & 1;
265   coded_size += pad;
266 
267   // Emit headers and partition #0
268   {
269     const uint8_t* const part0 = VP8BitWriterBuf(bw);
270     const size_t size0 = VP8BitWriterSize(bw);
271     ok = ok && PutHeader(enc->profile_, size0, coded_size, pic)
272             && pic->writer(part0, size0, pic)
273             && EmitPartitionsSize(enc, pic);
274     free((void*)part0);
275   }
276 
277   // Token partitions
278   for (p = 0; p < enc->num_parts_; ++p) {
279     const uint8_t* const buf = VP8BitWriterBuf(enc->parts_ + p);
280     const size_t size = VP8BitWriterSize(enc->parts_ + p);
281     if (size)
282       ok = ok && pic->writer(buf, size, pic);
283     free((void*)buf);
284   }
285 
286   // Padding byte
287   if (ok && pad) {
288     const uint8_t pad_byte[1] = { 0 };
289     ok = pic->writer(pad_byte, 1, pic);
290   }
291 
292   enc->coded_size_ = coded_size + KRIFF_SIZE;
293   return ok;
294 }
295 
296 //-----------------------------------------------------------------------------
297 
298 #if defined(__cplusplus) || defined(c_plusplus)
299 }    // extern "C"
300 #endif
301