1 //
2 //   Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
3 //   Free Software Foundation, Inc.
4 //   Copyright (C) 1999-2008 the VideoLAN team
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA    02110-1301    USA
19 
20 #ifdef HAVE_CONFIG_H
21 #include "gnashconfig.h"
22 #endif
23 
24 #include "VideoConverterFfmpeg.h"
25 #include "GnashException.h"
26 #include "ffmpegHeaders.h"
27 #include "log.h"
28 
29 namespace gnash {
30 namespace media {
31 namespace ffmpeg {
32 
33 #ifdef HAVE_SWSCALE_H
34 /// A wrapper round an SwsContext that ensures it's
35 /// freed on destruction.
36 class SwsContextWrapper
37 {
38 public:
39 
SwsContextWrapper(SwsContext * context)40     SwsContextWrapper(SwsContext* context)
41         :
42         _context(context)
43     {}
44 
~SwsContextWrapper()45     ~SwsContextWrapper()
46     {
47          sws_freeContext(_context);
48     }
49 
getContext() const50     SwsContext* getContext() const { return _context; }
51 
52 private:
53     SwsContext* _context;
54 
55 };
56 #endif
57 
58 // The lookup table in this function is adapted from chroma.c from the VLC
59 // codebase; its license permits distribution under GPLv3 and later.
60 AVPixelFormat
fourcc_to_ffmpeg(ImgBuf::Type4CC code)61 fourcc_to_ffmpeg(ImgBuf::Type4CC code)
62 {
63 
64 #define GNASH_FOURCC( a, b, c, d ) \
65               ( ((uint32_t)a) | ( ((uint32_t)b) << 8 ) \
66               | ( ((uint32_t)c) << 16 ) | ( ((uint32_t)d) << 24 ) )
67 
68     static const struct
69     {
70         ImgBuf::Type4CC  fourcc;
71         AVPixelFormat ffmpegcode;
72     } pixfmt_table[] =
73     {
74         // Planar YUV formats
75         {GNASH_FOURCC('I','4','4','4'), AV_PIX_FMT_YUV444P},
76         {GNASH_FOURCC('J','4','4','4'), AV_PIX_FMT_YUVJ444P},
77 
78 #if LIBAVUTIL_VERSION_INT >= ((49<<16)+(5<<8)+0)
79         {GNASH_FOURCC('I','4','4','0'), AV_PIX_FMT_YUV440P},
80         {GNASH_FOURCC('J','4','4','0'), AV_PIX_FMT_YUVJ440P},
81 #endif
82 
83         {GNASH_FOURCC('I','4','2','2'), AV_PIX_FMT_YUV422P},
84         {GNASH_FOURCC('J','4','2','2'), AV_PIX_FMT_YUVJ422P},
85 
86         {GNASH_FOURCC('I','4','2','0'), AV_PIX_FMT_YUV420P},
87         {GNASH_FOURCC('Y','V','1','2'), AV_PIX_FMT_YUV420P},
88         {GNASH_FOURCC('I','Y','U','V'), AV_PIX_FMT_YUV420P},
89         {GNASH_FOURCC('J','4','2','0'), AV_PIX_FMT_YUVJ420P},
90         {GNASH_FOURCC('I','4','1','1'), AV_PIX_FMT_YUV411P},
91         {GNASH_FOURCC('I','4','1','0'), AV_PIX_FMT_YUV410P},
92         {GNASH_FOURCC('Y','V','U','9'), AV_PIX_FMT_YUV410P},
93 
94 #if LIBAVUTIL_VERSION_INT >= ((49<<16)+(0<<8)+1)
95         {GNASH_FOURCC('N','V','1','2'), AV_PIX_FMT_NV12},
96         {GNASH_FOURCC('N','V','2','1'), AV_PIX_FMT_NV21},
97 #endif
98 
99         {GNASH_FOURCC('Y','U','Y','2'), AV_PIX_FMT_YUYV422},
100         {GNASH_FOURCC('Y','U','Y','V'), AV_PIX_FMT_YUYV422},
101         {GNASH_FOURCC('U','Y','V','Y'), AV_PIX_FMT_UYVY422},
102         {GNASH_FOURCC('Y','4','1','1'), AV_PIX_FMT_UYYVYY411},
103 
104         { 0, AV_PIX_FMT_NONE}
105     };
106 #undef GNASH_FOURCC
107 
108     for (int i = 0; pixfmt_table[i].fourcc != 0; i++ ) {
109 
110         if (pixfmt_table[i].fourcc == code) {
111             return pixfmt_table[i].ffmpegcode;
112         }
113     }
114 
115     return AV_PIX_FMT_NONE;
116 }
117 
VideoConverterFfmpeg(ImgBuf::Type4CC srcFormat,ImgBuf::Type4CC dstFormat)118 VideoConverterFfmpeg::VideoConverterFfmpeg(ImgBuf::Type4CC srcFormat, ImgBuf::Type4CC dstFormat)
119     : VideoConverter(srcFormat, dstFormat)
120 {
121      if(fourcc_to_ffmpeg(_dst_fmt) == AV_PIX_FMT_NONE) {
122          throw MediaException(_("VideoConverterFfmpeg cannot convert to the "
123                               "requested format"));
124      }
125 }
126 
~VideoConverterFfmpeg()127 VideoConverterFfmpeg::~VideoConverterFfmpeg()
128 {
129 
130 }
131 
132 
133 std::unique_ptr<ImgBuf>
convert(const ImgBuf & src)134 VideoConverterFfmpeg::convert(const ImgBuf& src)
135 {
136     std::unique_ptr<ImgBuf> ret;
137 
138     const int width = src.width;
139     const int height = src.height;
140 
141     AVPixelFormat dst_pixFmt = fourcc_to_ffmpeg(_dst_fmt);
142     assert(dst_pixFmt != AV_PIX_FMT_NONE);
143     AVPixelFormat src_pixFmt = AV_PIX_FMT_RGB24;
144 
145 #ifdef HAVE_SWSCALE_H
146 
147     if (!_swsContext.get()) {
148 
149         _swsContext.reset(new SwsContextWrapper(sws_getContext(width, height,
150             src_pixFmt, width, height, dst_pixFmt, SWS_BILINEAR, nullptr, nullptr,
151             nullptr)));
152 
153         if (!_swsContext->getContext()) {
154 
155             // This means we will try to assign the
156             // context again next time.
157             _swsContext.reset();
158 
159             return ret;
160         }
161     }
162 #endif
163 
164 
165     AVPicture srcpicture = {{src.data, nullptr, nullptr, nullptr},
166         {static_cast<int>(src.stride[0]), 0, 0, 0}};
167 
168 
169     int bufsize = avpicture_get_size(dst_pixFmt, width, height);
170     if (bufsize == -1) {
171         return ret;
172     }
173 
174     std::uint8_t* dstbuffer = new std::uint8_t[bufsize];
175 
176     AVPicture dstpicture;
177     avpicture_fill(&dstpicture, dstbuffer, dst_pixFmt, width, height);
178 
179 
180 #ifndef HAVE_SWSCALE_H
181     img_convert(&dstpicture, dst_pixFmt, &srcpicture, src_pixFmt, width,
182                 height);
183 #else
184 
185     int rv = sws_scale(_swsContext->getContext(), srcpicture.data,
186                        srcpicture.linesize, 0, height, dstpicture.data,
187                        dstpicture.linesize);
188 
189     if (rv == -1) {
190         return ret;
191     }
192 #endif
193     ret.reset(new ImgBuf(_dst_fmt, dstbuffer, bufsize, src.width,
194                          src.height));
195     std::copy(dstpicture.linesize, dstpicture.linesize+4, ret->stride.begin());
196 
197     return ret;
198 }
199 
200 
201 
202 } // gnash.media.ffmpeg namespace
203 } // gnash.media namespace
204 } // gnash namespace
205