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