1 /*
2  * libjingle
3  * Copyright 2010 Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "talk/media/base/videocommon.h"
29 
30 #include <limits.h>  // For INT_MAX
31 #include <math.h>
32 #include <sstream>
33 
34 #include "webrtc/base/arraysize.h"
35 #include "webrtc/base/common.h"
36 
37 namespace cricket {
38 
39 struct FourCCAliasEntry {
40   uint32_t alias;
41   uint32_t canonical;
42 };
43 
44 static const FourCCAliasEntry kFourCCAliases[] = {
45   {FOURCC_IYUV, FOURCC_I420},
46   {FOURCC_YU16, FOURCC_I422},
47   {FOURCC_YU24, FOURCC_I444},
48   {FOURCC_YUYV, FOURCC_YUY2},
49   {FOURCC_YUVS, FOURCC_YUY2},
50   {FOURCC_HDYC, FOURCC_UYVY},
51   {FOURCC_2VUY, FOURCC_UYVY},
52   {FOURCC_JPEG, FOURCC_MJPG},  // Note: JPEG has DHT while MJPG does not.
53   {FOURCC_DMB1, FOURCC_MJPG},
54   {FOURCC_BA81, FOURCC_BGGR},
55   {FOURCC_RGB3, FOURCC_RAW},
56   {FOURCC_BGR3, FOURCC_24BG},
57   {FOURCC_CM32, FOURCC_BGRA},
58   {FOURCC_CM24, FOURCC_RAW},
59 };
60 
CanonicalFourCC(uint32_t fourcc)61 uint32_t CanonicalFourCC(uint32_t fourcc) {
62   for (int i = 0; i < arraysize(kFourCCAliases); ++i) {
63     if (kFourCCAliases[i].alias == fourcc) {
64       return kFourCCAliases[i].canonical;
65     }
66   }
67   // Not an alias, so return it as-is.
68   return fourcc;
69 }
70 
71 static float kScaleFactors[] = {
72   1.f / 1.f,  // Full size.
73   1.f / 2.f,  // 1/2 scale.
74   1.f / 4.f,  // 1/4 scale.
75   1.f / 8.f,  // 1/8 scale.
76   1.f / 16.f  // 1/16 scale.
77 };
78 
79 static const int kNumScaleFactors = arraysize(kScaleFactors);
80 
81 // Finds the scale factor that, when applied to width and height, produces
82 // fewer than num_pixels.
FindLowerScale(int width,int height,int target_num_pixels)83 static float FindLowerScale(int width, int height, int target_num_pixels) {
84   if (!target_num_pixels) {
85     return 0.f;
86   }
87   int best_distance = INT_MAX;
88   int best_index = kNumScaleFactors - 1;  // Default to max scale.
89   for (int i = 0; i < kNumScaleFactors; ++i) {
90     int test_num_pixels = static_cast<int>(width * kScaleFactors[i] *
91                                            height * kScaleFactors[i]);
92     int diff = target_num_pixels - test_num_pixels;
93     if (diff >= 0 && diff < best_distance) {
94       best_distance = diff;
95       best_index = i;
96       if (best_distance == 0) {  // Found exact match.
97         break;
98       }
99     }
100   }
101   return kScaleFactors[best_index];
102 }
103 
104 // Computes a scale less to fit in max_pixels while maintaining aspect ratio.
ComputeScaleMaxPixels(int frame_width,int frame_height,int max_pixels,int * scaled_width,int * scaled_height)105 void ComputeScaleMaxPixels(int frame_width, int frame_height, int max_pixels,
106     int* scaled_width, int* scaled_height) {
107   ASSERT(scaled_width != NULL);
108   ASSERT(scaled_height != NULL);
109   ASSERT(max_pixels > 0);
110   const int kMaxWidth = 4096;
111   const int kMaxHeight = 3072;
112   int new_frame_width = frame_width;
113   int new_frame_height = frame_height;
114 
115   // Limit width.
116   if (new_frame_width > kMaxWidth) {
117     new_frame_height = new_frame_height * kMaxWidth / new_frame_width;
118     new_frame_width = kMaxWidth;
119   }
120   // Limit height.
121   if (new_frame_height > kMaxHeight) {
122     new_frame_width = new_frame_width * kMaxHeight / new_frame_height;
123     new_frame_height = kMaxHeight;
124   }
125   // Limit number of pixels.
126   if (new_frame_width * new_frame_height > max_pixels) {
127     // Compute new width such that width * height is less than maximum but
128     // maintains original captured frame aspect ratio.
129     new_frame_width = static_cast<int>(sqrtf(static_cast<float>(
130         max_pixels) * new_frame_width / new_frame_height));
131     new_frame_height = max_pixels / new_frame_width;
132   }
133   // Snap to a scale factor that is less than or equal to target pixels.
134   float scale = FindLowerScale(frame_width, frame_height,
135                                new_frame_width * new_frame_height);
136   *scaled_width = static_cast<int>(frame_width * scale + .5f);
137   *scaled_height = static_cast<int>(frame_height * scale + .5f);
138 }
139 
140 // Compute a size to scale frames to that is below maximum compression
141 // and rendering size with the same aspect ratio.
ComputeScale(int frame_width,int frame_height,int fps,int * scaled_width,int * scaled_height)142 void ComputeScale(int frame_width, int frame_height, int fps,
143                   int* scaled_width, int* scaled_height) {
144   // Maximum pixels limit is set to Retina MacBookPro 15" resolution of
145   // 2880 x 1800 as of 4/18/2013.
146   // For high fps, maximum pixels limit is set based on common 24" monitor
147   // resolution of 2048 x 1280 as of 6/13/2013. The Retina resolution is
148   // therefore reduced to 1440 x 900.
149   int max_pixels = (fps > 5) ? 2048 * 1280 : 2880 * 1800;
150   ComputeScaleMaxPixels(
151       frame_width, frame_height, max_pixels, scaled_width, scaled_height);
152 }
153 
154 // Compute size to crop video frame to.
155 // If cropped_format_* is 0, return the frame_* size as is.
ComputeCrop(int cropped_format_width,int cropped_format_height,int frame_width,int frame_height,int pixel_width,int pixel_height,int rotation,int * cropped_width,int * cropped_height)156 void ComputeCrop(int cropped_format_width, int cropped_format_height,
157                  int frame_width, int frame_height,
158                  int pixel_width, int pixel_height,
159                  int rotation,
160                  int* cropped_width, int* cropped_height) {
161   // Transform screen crop to camera space if rotated.
162   if (rotation == 90 || rotation == 270) {
163     std::swap(cropped_format_width, cropped_format_height);
164   }
165   ASSERT(cropped_format_width >= 0);
166   ASSERT(cropped_format_height >= 0);
167   ASSERT(frame_width > 0);
168   ASSERT(frame_height > 0);
169   ASSERT(pixel_width >= 0);
170   ASSERT(pixel_height >= 0);
171   ASSERT(rotation == 0 || rotation == 90 || rotation == 180 || rotation == 270);
172   ASSERT(cropped_width != NULL);
173   ASSERT(cropped_height != NULL);
174   if (!pixel_width) {
175     pixel_width = 1;
176   }
177   if (!pixel_height) {
178     pixel_height = 1;
179   }
180   // if cropped_format is 0x0 disable cropping.
181   if (!cropped_format_height) {
182     cropped_format_height = 1;
183   }
184   float frame_aspect = static_cast<float>(frame_width * pixel_width) /
185       static_cast<float>(frame_height * pixel_height);
186   float crop_aspect = static_cast<float>(cropped_format_width) /
187       static_cast<float>(cropped_format_height);
188   // kAspectThresh is the maximum aspect ratio difference that we'll accept
189   // for cropping.  The value 1.34 allows cropping from 4:3 to 16:9.
190   // Set to zero to disable cropping entirely.
191   // TODO(fbarchard): crop to multiple of 16 width for better performance.
192   const float kAspectThresh = 1.34f;
193   // Wide aspect - crop horizontally
194   if (frame_aspect > crop_aspect &&
195       frame_aspect < crop_aspect * kAspectThresh) {
196     // Round width down to multiple of 4 to avoid odd chroma width.
197     // Width a multiple of 4 allows a half size image to have chroma channel
198     // that avoids rounding errors.
199     frame_width = static_cast<int>((crop_aspect * frame_height *
200         pixel_height) / pixel_width + 0.5f) & ~3;
201   } else if (frame_aspect < crop_aspect &&
202              frame_aspect > crop_aspect / kAspectThresh) {
203     frame_height = static_cast<int>((frame_width * pixel_width) /
204         (crop_aspect * pixel_height) + 0.5f) & ~1;
205   }
206   *cropped_width = frame_width;
207   *cropped_height = frame_height;
208 }
209 
210 // Compute the frame size that makes pixels square pixel aspect ratio.
ComputeScaleToSquarePixels(int in_width,int in_height,int pixel_width,int pixel_height,int * scaled_width,int * scaled_height)211 void ComputeScaleToSquarePixels(int in_width, int in_height,
212                                 int pixel_width, int pixel_height,
213                                 int* scaled_width, int* scaled_height) {
214   *scaled_width = in_width;  // Keep width the same.
215   *scaled_height = in_height * pixel_height / pixel_width;
216 }
217 
218 // The C++ standard requires a namespace-scope definition of static const
219 // integral types even when they are initialized in the declaration (see
220 // [class.static.data]/4), but MSVC with /Ze is non-conforming and treats that
221 // as a multiply defined symbol error. See Also:
222 // http://msdn.microsoft.com/en-us/library/34h23df8.aspx
223 #ifndef _MSC_EXTENSIONS
224 const int64_t VideoFormat::kMinimumInterval;  // Initialized in header.
225 #endif
226 
ToString() const227 std::string VideoFormat::ToString() const {
228   std::string fourcc_name = GetFourccName(fourcc) + " ";
229   for (std::string::const_iterator i = fourcc_name.begin();
230       i < fourcc_name.end(); ++i) {
231     // Test character is printable; Avoid isprint() which asserts on negatives.
232     if (*i < 32 || *i >= 127) {
233       fourcc_name = "";
234       break;
235     }
236   }
237 
238   std::ostringstream ss;
239   ss << fourcc_name << width << "x" << height << "x"
240      << IntervalToFpsFloat(interval);
241   return ss.str();
242 }
243 
244 }  // namespace cricket
245