1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright@ Samsung Electronics Co. LTD
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 /*!
19  * \file      libscaler-v4l2.cpp
20  * \brief     source file for Scaler HAL
21  * \author    Cho KyongHo <pullip.cho@samsung.com>
22  * \date      2014/05/12
23  *
24  * <b>Revision History: </b>
25  * - 2014.05.12 : Cho KyongHo (pullip.cho@samsung.com) \n
26  *   Create
27  */
28 
29 #include <cstring>
30 #include <cstdlib>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <sys/ioctl.h>
34 #include <sys/mman.h>
35 
36 #include "libscaler-v4l2.h"
37 #include "libscaler-swscaler.h"
38 
39 
40 #define V4L2_CID_EXYNOS_BASE            (V4L2_CTRL_CLASS_USER | 0x2000)
41 #define V4L2_CID_CSC_EQ_MODE            (V4L2_CID_EXYNOS_BASE + 100)
42 #define V4L2_CID_CSC_EQ                 (V4L2_CID_EXYNOS_BASE + 101)
43 #define V4L2_CID_CSC_RANGE              (V4L2_CID_EXYNOS_BASE + 102)
44 #define V4L2_CID_CONTENT_PROTECTION     (V4L2_CID_EXYNOS_BASE + 201)
45 
Initialize(int instance)46 void CScalerV4L2::Initialize(int instance)
47 {
48     snprintf(m_cszNode, SC_MAX_NODENAME, SC_DEV_NODE "%d", SC_NODE(instance));
49 
50     m_fdScaler = open(m_cszNode, O_RDWR);
51     if (m_fdScaler < 0) {
52         SC_LOGERR("Failed to open '%s'", m_cszNode);
53         return;
54     }
55 
56     m_fdValidate = -m_fdScaler;
57 }
58 
CScalerV4L2(int instance,int allow_drm)59 CScalerV4L2::CScalerV4L2(int instance, int allow_drm)
60 {
61     m_fdScaler = -1;
62     m_iInstance = instance;
63     m_nRotDegree = 0;
64     m_fStatus = 0;
65     m_filter = 0;
66 
67     memset(&m_frmSrc, 0, sizeof(m_frmSrc));
68     memset(&m_frmDst, 0, sizeof(m_frmDst));
69 
70     m_frmSrc.fdAcquireFence = -1;
71     m_frmDst.fdAcquireFence = -1;
72 
73     m_frmSrc.name = "output";
74     m_frmDst.name = "capture";
75 
76     m_frmSrc.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
77     m_frmDst.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
78 
79     m_frameRate = 0;
80 
81     Initialize(instance);
82 
83     if(Valid()) {
84         if (allow_drm)
85             SetFlag(m_fStatus, SCF_ALLOW_DRM);
86         SC_LOGD("Successfully opened '%s'; returned fd %d; drmmode %s",
87                 m_cszNode, m_fdScaler, allow_drm ? "enabled" : "disabled");
88     }
89 }
90 
~CScalerV4L2()91 CScalerV4L2::~CScalerV4L2()
92 {
93     if (m_fdScaler >= 0)
94         close(m_fdScaler);
95 
96     m_fdScaler = -1;
97 }
98 
Stop()99 bool CScalerV4L2::Stop()
100 {
101     if (!ResetDevice(m_frmSrc)) {
102         SC_LOGE("Failed to stop Scaler for the output frame");
103         return false;
104     }
105 
106     if (!ResetDevice(m_frmDst)) {
107         SC_LOGE("Failed to stop Scaler for the cature frame");
108         return false;
109     }
110 
111     return true;
112 }
113 
Run()114 bool CScalerV4L2::Run()
115 {
116     if (LibScaler::UnderOne16thScaling(
117                 m_frmSrc.crop.width, m_frmSrc.crop.height,
118                 m_frmDst.crop.width, m_frmDst.crop.height,
119                 m_nRotDegree))
120         return RunSWScaling();
121 
122     if (!DevSetCtrl())
123         return false;
124 
125     if (!DevSetFormat())
126         return false;
127 
128     if (!ReqBufs())
129         return false;
130 
131     if (!StreamOn())
132         return false;
133 
134     if (!QBuf()) {
135         Stop();
136         return false;
137     }
138 
139     return DQBuf();
140 }
141 
SetCtrl()142 bool CScalerV4L2::SetCtrl()
143 {
144     struct v4l2_control ctrl;
145 
146     if (TestFlag(m_fStatus, SCF_DRM_FRESH)) {
147         if (!Stop())
148             return false;
149 
150         ctrl.id = V4L2_CID_CONTENT_PROTECTION;
151         ctrl.value = TestFlag(m_fStatus, SCF_DRM);
152         if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) {
153             SC_LOGERR("Failed configure V4L2_CID_CONTENT_PROTECTION to %d", TestFlag(m_fStatus, SCF_DRM));
154             return false;
155         }
156 
157         ClearFlag(m_fStatus, SCF_DRM_FRESH);
158     } else {
159         SC_LOGD("Skipping DRM configuration");
160     }
161 
162     if (TestFlag(m_fStatus, SCF_ROTATION_FRESH)) {
163         if (!Stop())
164             return false;
165 
166         ctrl.id = V4L2_CID_ROTATE;
167         ctrl.value = m_nRotDegree;
168         if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) {
169             SC_LOGERR("Failed V4L2_CID_ROTATE with degree %d", m_nRotDegree);
170             return false;
171         }
172 
173         ctrl.id = V4L2_CID_VFLIP;
174         ctrl.value = TestFlag(m_fStatus, SCF_HFLIP);
175         if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) {
176             SC_LOGERR("Failed V4L2_CID_VFLIP - %d", TestFlag(m_fStatus, SCF_VFLIP));
177             return false;
178         }
179 
180         ctrl.id = V4L2_CID_HFLIP;
181         ctrl.value = TestFlag(m_fStatus, SCF_VFLIP);
182         if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) {
183             SC_LOGERR("Failed V4L2_CID_HFLIP - %d", TestFlag(m_fStatus, SCF_HFLIP));
184             return false;
185         }
186 
187         SC_LOGD("Successfully set CID_ROTATE(%d), CID_VFLIP(%d) and CID_HFLIP(%d)",
188                 m_nRotDegree, TestFlag(m_fStatus, SCF_VFLIP), TestFlag(m_fStatus, SCF_HFLIP));
189         ClearFlag(m_fStatus, SCF_ROTATION_FRESH);
190     } else {
191         SC_LOGD("Skipping rotation and flip setting due to no change");
192     }
193 
194     if (m_filter > 0) {
195         if (!Stop())
196             return false;
197 
198         ctrl.id = LIBSC_V4L2_CID_DNOISE_FT;
199         ctrl.value = m_filter;
200         if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) {
201             SC_LOGERR("Failed LIBSC_V4L2_CID_DNOISE_FT to %d", m_filter);
202             return false;
203         }
204     }
205 
206     if (TestFlag(m_fStatus, SCF_CSC_FRESH)) {
207         if (!Stop())
208             return false;
209 
210         ctrl.id = V4L2_CID_CSC_RANGE;
211         ctrl.value = TestFlag(m_fStatus, SCF_CSC_WIDE) ? 1 : 0;
212         if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) {
213             SC_LOGERR("Failed V4L2_CID_CSC_RANGE to %d", TestFlag(m_fStatus, SCF_CSC_WIDE));
214             return false;
215         }
216 
217         ctrl.id = V4L2_CID_CSC_EQ;
218         ctrl.value = m_colorspace;
219         if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) {
220             SC_LOGERR("Failed V4L2_CID_CSC_EQ to %d", m_colorspace);
221         }
222         ClearFlag(m_fStatus, SCF_CSC_FRESH);
223     }
224 
225     /* This is optional, so we don't return failure. */
226     if (TestFlag(m_fStatus, SCF_FRAMERATE)) {
227         if (!Stop())
228             return false;
229 
230         ctrl.id = SC_CID_FRAMERATE;
231         ctrl.value = m_frameRate;
232         if (ioctl(m_fdScaler, VIDIOC_S_CTRL, &ctrl) < 0) {
233             SC_LOGD("Failed SC_CID_FRAMERATE to %d", m_frameRate);
234         }
235         ClearFlag(m_fStatus, SCF_FRAMERATE);
236     }
237 
238     return true;
239 }
240 
DevSetCtrl()241 bool CScalerV4L2::DevSetCtrl()
242 {
243     return SetCtrl();
244 }
245 
ResetDevice(FrameInfo & frm)246 bool CScalerV4L2::ResetDevice(FrameInfo &frm)
247 {
248     DQBuf(frm);
249 
250     if (TestFlag(frm.flags, SCFF_STREAMING)) {
251         if (ioctl(m_fdScaler, VIDIOC_STREAMOFF, &frm.type) < 0) {
252             SC_LOGERR("Failed STREAMOFF for the %s", frm.name);
253         }
254         ClearFlag(frm.flags, SCFF_STREAMING);
255     }
256 
257     SC_LOGD("VIDIC_STREAMOFF is successful for the %s", frm.name);
258 
259     if (TestFlag(frm.flags, SCFF_REQBUFS)) {
260         v4l2_requestbuffers reqbufs;
261         memset(&reqbufs, 0, sizeof(reqbufs));
262         reqbufs.type = frm.type;
263         reqbufs.memory = frm.memory;
264         if (ioctl(m_fdScaler, VIDIOC_REQBUFS, &reqbufs) < 0 ) {
265             SC_LOGERR("Failed to REQBUFS(0) for the %s", frm.name);
266         }
267 
268         ClearFlag(frm.flags, SCFF_REQBUFS);
269     }
270 
271     SC_LOGD("VIDIC_REQBUFS(0) is successful for the %s", frm.name);
272 
273     return true;
274 }
275 
DevSetFormat(FrameInfo & frm)276 bool CScalerV4L2::DevSetFormat(FrameInfo &frm)
277 {
278 
279     if (!TestFlag(frm.flags, SCFF_BUF_FRESH)) {
280         SC_LOGD("Skipping S_FMT for the %s since it is already done", frm.name);
281         return true;
282     }
283 
284     if (!ResetDevice(frm)) {
285         SC_LOGE("Failed to VIDIOC_S_FMT for the %s", frm.name);
286         return false;
287     }
288 
289     v4l2_format fmt;
290     memset(&fmt, 0, sizeof(fmt));
291     fmt.type = frm.type;
292     fmt.fmt.pix_mp.pixelformat = frm.color_format;
293     fmt.fmt.pix_mp.width  = frm.width;
294     fmt.fmt.pix_mp.height = frm.height;
295 
296     if (TestFlag(frm.flags, SCFF_PREMULTIPLIED)) {
297 #ifdef SCALER_USE_PREMUL_FMT
298         fmt.fmt.pix_mp.flags = V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
299 #else
300         fmt.fmt.pix_mp.reserved[1] = SC_V4L2_FMT_PREMULTI_FLAG;
301 #endif
302     }
303 
304     if (ioctl(m_fdScaler, VIDIOC_S_FMT, &fmt) < 0) {
305         SC_LOGERR("Failed S_FMT(fmt: %d, w:%d, h:%d) for the %s",
306                 fmt.fmt.pix_mp.pixelformat, fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
307                 frm.name);
308         return false;
309     }
310 
311     // returned fmt.fmt.pix_mp.num_planes and fmt.fmt.pix_mp.plane_fmt[i].sizeimage
312     frm.out_num_planes = fmt.fmt.pix_mp.num_planes;
313 
314     for (int i = 0; i < frm.out_num_planes; i++)
315         frm.out_plane_size[i] = fmt.fmt.pix_mp.plane_fmt[i].sizeimage;
316 
317     v4l2_crop crop;
318     crop.type = frm.type;
319     crop.c = frm.crop;
320 
321     if (ioctl(m_fdScaler, VIDIOC_S_CROP, &crop) < 0) {
322         SC_LOGERR("Failed S_CROP(fmt: %d, l:%d, t:%d, w:%d, h:%d) for the %s",
323                 crop.type, crop.c.left, crop.c.top, crop.c.width, crop.c.height,
324                 frm.name);
325         return false;
326     }
327 
328     if (frm.out_num_planes > SC_MAX_PLANES) {
329         SC_LOGE("Number of planes exceeds %d of %s", frm.out_num_planes, frm.name);
330         return false;
331     }
332 
333     ClearFlag(frm.flags, SCFF_BUF_FRESH);
334 
335     SC_LOGD("Successfully S_FMT and S_CROP for the %s", frm.name);
336 
337     return true;
338 }
339 
DevSetFormat()340 bool CScalerV4L2::DevSetFormat()
341 {
342     if (!DevSetFormat(m_frmSrc))
343         return false;
344 
345     return DevSetFormat(m_frmDst);
346 }
347 
QBuf(FrameInfo & frm,int * pfdReleaseFence)348 bool CScalerV4L2::QBuf(FrameInfo &frm, int *pfdReleaseFence)
349 {
350     v4l2_buffer buffer;
351     v4l2_plane planes[SC_MAX_PLANES];
352 
353     if (!TestFlag(frm.flags, SCFF_REQBUFS)) {
354         SC_LOGE("Trying to QBUF without REQBUFS for %s is not allowed",
355                 frm.name);
356         return false;
357     }
358 
359     if (!DQBuf(frm))
360         return false;
361 
362     memset(&buffer, 0, sizeof(buffer));
363     memset(&planes, 0, sizeof(planes));
364 
365     buffer.type   = frm.type;
366     buffer.memory = frm.memory;
367     buffer.index  = 0;
368     buffer.length = frm.out_num_planes;
369 
370     if (pfdReleaseFence) {
371         buffer.flags    = V4L2_BUF_FLAG_USE_SYNC;
372         buffer.reserved = frm.fdAcquireFence;
373     }
374 
375     buffer.m.planes = planes;
376     for (unsigned long i = 0; i < buffer.length; i++) {
377         planes[i].length = frm.out_plane_size[i];
378         if (V4L2_TYPE_IS_OUTPUT(buffer.type))
379             planes[i].bytesused = planes[i].length;
380         if (buffer.memory == V4L2_MEMORY_DMABUF)
381             planes[i].m.fd = static_cast<__s32>(reinterpret_cast<long>(frm.addr[i]));
382         else
383             planes[i].m.userptr = reinterpret_cast<unsigned long>(frm.addr[i]);
384     }
385 
386 
387     if (ioctl(m_fdScaler, VIDIOC_QBUF, &buffer) < 0) {
388         SC_LOGERR("Failed to QBUF for the %s", frm.name);
389         return false;
390     }
391 
392     SetFlag(frm.flags, SCFF_QBUF);
393 
394     if (pfdReleaseFence) {
395         if (frm.fdAcquireFence >= 0)
396             close(frm.fdAcquireFence);
397         frm.fdAcquireFence = -1;
398 
399         *pfdReleaseFence = static_cast<int>(buffer.reserved);
400     }
401 
402     SC_LOGD("Successfully QBUF for the %s", frm.name);
403 
404     return true;
405 }
406 
ReqBufs(FrameInfo & frm)407 bool CScalerV4L2::ReqBufs(FrameInfo &frm)
408 {
409     v4l2_requestbuffers reqbufs;
410 
411     if (TestFlag(frm.flags, SCFF_REQBUFS)) {
412         SC_LOGD("Skipping REQBUFS for the %s since it is already done", frm.name);
413         return true;
414     }
415 
416     memset(&reqbufs, 0, sizeof(reqbufs));
417 
418     reqbufs.type    = frm.type;
419     reqbufs.memory  = frm.memory;
420     reqbufs.count   = 1;
421 
422     if (ioctl(m_fdScaler, VIDIOC_REQBUFS, &reqbufs) < 0) {
423         SC_LOGERR("Failed to REQBUFS for the %s", frm.name);
424         return false;
425     }
426 
427     SetFlag(frm.flags, SCFF_REQBUFS);
428 
429     SC_LOGD("Successfully REQBUFS for the %s", frm.name);
430 
431     return true;
432 }
433 
SetRotate(int rot,int flip_h,int flip_v)434 bool CScalerV4L2::SetRotate(int rot, int flip_h, int flip_v)
435 {
436     if ((rot % 90) != 0) {
437         SC_LOGE("Rotation of %d degree is not supported", rot);
438         return false;
439     }
440 
441     SetRotDegree(rot);
442 
443     if (flip_h)
444         SetFlag(m_fStatus, SCF_VFLIP);
445     else
446         ClearFlag(m_fStatus, SCF_VFLIP);
447 
448     if (flip_v)
449         SetFlag(m_fStatus, SCF_HFLIP);
450     else
451         ClearFlag(m_fStatus, SCF_HFLIP);
452 
453     SetFlag(m_fStatus, SCF_ROTATION_FRESH);
454 
455     return true;
456 }
457 
StreamOn(FrameInfo & frm)458 bool CScalerV4L2::StreamOn(FrameInfo &frm)
459 {
460     if (!TestFlag(frm.flags, SCFF_REQBUFS)) {
461         SC_LOGE("Trying to STREAMON without REQBUFS for %s is not allowed",
462                 frm.name);
463         return false;
464     }
465 
466     if (!TestFlag(frm.flags, SCFF_STREAMING)) {
467         if (ioctl(m_fdScaler, VIDIOC_STREAMON, &frm.type) < 0 ) {
468             SC_LOGERR("Failed StreamOn for the %s", frm.name);
469             return false;
470         }
471 
472         SetFlag(frm.flags, SCFF_STREAMING);
473 
474         SC_LOGD("Successfully VIDIOC_STREAMON for the %s", frm.name);
475     }
476 
477     return true;
478 }
479 
DQBuf(FrameInfo & frm)480 bool CScalerV4L2::DQBuf(FrameInfo &frm)
481 {
482     if (!TestFlag(frm.flags, SCFF_QBUF))
483         return true;
484 
485     v4l2_buffer buffer;
486     v4l2_plane plane[SC_NUM_OF_PLANES];
487 
488     memset(&buffer, 0, sizeof(buffer));
489 
490     buffer.type = frm.type;
491     buffer.memory = frm.memory;
492 
493     if (V4L2_TYPE_IS_MULTIPLANAR(buffer.type)) {
494         memset(plane, 0, sizeof(plane));
495 
496         buffer.length = frm.out_num_planes;
497         buffer.m.planes = plane;
498     }
499 
500     ClearFlag(frm.flags, SCFF_QBUF);
501 
502     if (ioctl(m_fdScaler, VIDIOC_DQBUF, &buffer) < 0 ) {
503         SC_LOGERR("Failed to DQBuf the %s", frm.name);
504         return false;
505     }
506 
507     if (buffer.flags & V4L2_BUF_FLAG_ERROR) {
508         SC_LOGE("Error occurred while processing streaming data");
509         return false;
510     }
511 
512     SC_LOGD("Successfully VIDIOC_DQBUF for the %s", frm.name);
513 
514     return true;
515 }
516 
GetBuffer(CScalerV4L2::FrameInfo & frm,char * addr[])517 static bool GetBuffer(CScalerV4L2::FrameInfo &frm, char *addr[])
518 {
519     for (int i = 0; i < frm.out_num_planes; i++) {
520         if (frm.memory == V4L2_MEMORY_DMABUF) {
521             addr[i] = reinterpret_cast<char *>(mmap(NULL, frm.out_plane_size[i],
522                         PROT_READ | PROT_WRITE, MAP_SHARED,
523                         static_cast<int>(reinterpret_cast<long>(frm.addr[i])), 0));
524             if (addr[i] == MAP_FAILED) {
525                 SC_LOGE("Failed to map FD %ld", reinterpret_cast<long>(frm.addr[i]));
526                 while (i-- > 0)
527                     munmap(addr[i], frm.out_plane_size[i]);
528                 return false;
529             }
530         } else {
531             addr[i] = reinterpret_cast<char *>(frm.addr[i]);
532         }
533     }
534 
535     return true;
536 }
537 
PutBuffer(CScalerV4L2::FrameInfo & frm,char * addr[])538 static void PutBuffer(CScalerV4L2::FrameInfo &frm, char *addr[])
539 {
540     for (int i = 0; i < frm.out_num_planes; i++) {
541         if (frm.memory == V4L2_MEMORY_DMABUF) {
542             munmap(addr[i], frm.out_plane_size[i]);
543         }
544     }
545 }
546 
RunSWScaling()547 bool CScalerV4L2::RunSWScaling()
548 {
549     if (m_frmSrc.color_format != m_frmDst.color_format) {
550         SC_LOGE("Source and target image format must be the same");
551         return false;
552     }
553 
554     if (m_nRotDegree != 0) {
555         SC_LOGE("Rotation is not allowed for S/W Scaling");
556         return false;
557     }
558 
559     SC_LOGI("Running S/W Scaler: %dx%d -> %dx%d",
560             m_frmSrc.crop.width, m_frmSrc.crop.height,
561             m_frmDst.crop.width, m_frmDst.crop.height);
562 
563     CScalerSW *swsc;
564     char *src[3], *dst[3];
565 
566     switch (m_frmSrc.color_format) {
567         case V4L2_PIX_FMT_YUYV:
568         case V4L2_PIX_FMT_YVYU:
569             m_frmSrc.out_num_planes = 1;
570             m_frmSrc.out_plane_size[0] = m_frmSrc.width * m_frmSrc.height * 2;
571             m_frmDst.out_num_planes = 1;
572             m_frmDst.out_plane_size[0] = m_frmDst.width * m_frmDst.height * 2;
573 
574             if (!GetBuffer(m_frmSrc, src))
575                 return false;
576 
577             if (!GetBuffer(m_frmDst, dst)) {
578                 PutBuffer(m_frmSrc, src);
579                 return false;
580             }
581 
582             swsc = new CScalerSW_YUYV(src[0], dst[0]);
583             break;
584         case V4L2_PIX_FMT_NV12M:
585         case V4L2_PIX_FMT_NV21M:
586             m_frmSrc.out_num_planes = 2;
587             m_frmDst.out_num_planes = 2;
588             m_frmSrc.out_plane_size[0] = m_frmSrc.width * m_frmSrc.height;
589             m_frmDst.out_plane_size[0] = m_frmDst.width * m_frmDst.height;
590             m_frmSrc.out_plane_size[1] = m_frmSrc.out_plane_size[0] / 2;
591             m_frmDst.out_plane_size[1] = m_frmDst.out_plane_size[0] / 2;
592 
593             if (!GetBuffer(m_frmSrc, src))
594                 return false;
595 
596             if (!GetBuffer(m_frmDst, dst)) {
597                 PutBuffer(m_frmSrc, src);
598                 return false;
599             }
600 
601             swsc = new CScalerSW_NV12(src[0], src[1], dst[0], dst[1]);
602             break;
603         case V4L2_PIX_FMT_NV12:
604         case V4L2_PIX_FMT_NV21:
605             m_frmSrc.out_num_planes = 1;
606             m_frmDst.out_num_planes = 1;
607             m_frmSrc.out_plane_size[0] = m_frmSrc.width * m_frmSrc.height;
608             m_frmDst.out_plane_size[0] = m_frmDst.width * m_frmDst.height;
609             m_frmSrc.out_plane_size[0] += m_frmSrc.out_plane_size[0] / 2;
610             m_frmDst.out_plane_size[0] += m_frmDst.out_plane_size[0] / 2;
611 
612             if (!GetBuffer(m_frmSrc, src))
613                 return false;
614 
615             if (!GetBuffer(m_frmDst, dst)) {
616                 PutBuffer(m_frmSrc, src);
617                 return false;
618             }
619 
620             src[1] = src[0] + m_frmSrc.width * m_frmSrc.height;
621             dst[1] = dst[0] + m_frmDst.width * m_frmDst.height;
622 
623             swsc = new CScalerSW_NV12(src[0], src[1], dst[0], dst[1]);
624             break;
625         case V4L2_PIX_FMT_UYVY: // TODO: UYVY is not implemented yet.
626         default:
627             SC_LOGE("Format %x is not supported", m_frmSrc.color_format);
628             return false;
629     }
630 
631     if (swsc == NULL) {
632         SC_LOGE("Failed to allocate SW Scaler");
633         PutBuffer(m_frmSrc, src);
634         PutBuffer(m_frmDst, dst);
635         return false;
636     }
637 
638     swsc->SetSrcRect(m_frmSrc.crop.left, m_frmSrc.crop.top,
639             m_frmSrc.crop.width, m_frmSrc.crop.height, m_frmSrc.width);
640 
641     swsc->SetDstRect(m_frmDst.crop.left, m_frmDst.crop.top,
642             m_frmDst.crop.width, m_frmDst.crop.height, m_frmDst.width);
643 
644     bool ret = swsc->Scale();
645 
646     delete swsc;
647 
648     PutBuffer(m_frmSrc, src);
649     PutBuffer(m_frmDst, dst);
650 
651     return ret;
652 }
653