Merge remote-tracking branch 'upstream/3.4' into merge-3.4
[platform/upstream/opencv.git] / modules / videoio / src / cap_mfx_reader.cpp
1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html
4
5 #include "cap_mfx_reader.hpp"
6 #include "opencv2/core/base.hpp"
7 #include "cap_mfx_common.hpp"
8 #include "opencv2/imgproc/hal/hal.hpp"
9 #include "cap_interface.hpp"
10
11 using namespace cv;
12 using namespace std;
13
14 inline bool hasExtension(const String &filename, const String &ext)
15 {
16     if (filename.size() <= ext.size())
17         return false;
18     const size_t diff = filename.size() - ext.size();
19     const size_t found_at = filename.rfind(ext);
20     return found_at == diff;
21 }
22
23 inline mfxU32 determineCodecId(const String &filename)
24 {
25     if (hasExtension(filename, ".h264") || hasExtension(filename, ".264"))
26         return MFX_CODEC_AVC;
27     else if (hasExtension(filename, ".mp2") || hasExtension(filename, ".mpeg2"))
28         return MFX_CODEC_MPEG2;
29     else if (hasExtension(filename, ".265") || hasExtension(filename, ".hevc"))
30         return MFX_CODEC_HEVC;
31     else
32         return (mfxU32)-1;
33 }
34
35 //==========================================================================
36
37 VideoCapture_IntelMFX::VideoCapture_IntelMFX(const cv::String &filename)
38     : session(0), plugin(0), deviceHandler(0), bs(0), decoder(0), pool(0), outSurface(0), good(false)
39 {
40     mfxStatus res = MFX_ERR_NONE;
41
42     // Init device and session
43     deviceHandler = createDeviceHandler();
44     session = new MFXVideoSession();
45     if (!deviceHandler->init(*session))
46     {
47         MSG(cerr << "MFX: Can't initialize session" << endl);
48         return;
49     }
50
51     // Load appropriate plugin
52
53     mfxU32 codecId = determineCodecId(filename);
54     if (codecId == (mfxU32)-1)
55     {
56         MSG(cerr << "MFX: Unsupported extension: " << filename << endl);
57         return;
58     }
59     plugin = Plugin::loadDecoderPlugin(*session, codecId);
60     if (plugin && !plugin->isGood())
61     {
62         MSG(cerr << "MFX: LoadPlugin failed for codec: " << codecId << " (" << filename << ")" << endl);
63         return;
64     }
65
66     // Read some content from file
67
68     bs = new ReadBitstream(filename.c_str());
69     if (!bs->read())
70     {
71         MSG(cerr << "MFX: Failed to read bitstream" << endl);
72         return;
73     }
74
75     // Create decoder and decode stream header
76
77     decoder = new MFXVideoDECODE(*session);
78     mfxVideoParam params;
79     memset(&params, 0, sizeof(params));
80     params.mfx.CodecId = codecId;
81     params.IOPattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
82     res = decoder->DecodeHeader(&bs->stream, &params);
83     DBG(cout << "DecodeHeader: " << res << endl << params.mfx << params.mfx.FrameInfo << endl);
84     if (res < MFX_ERR_NONE)
85     {
86         MSG(cerr << "MFX: Failed to decode stream header: " << res << endl);
87         return;
88     }
89
90     // Adjust parameters
91
92     res = decoder->Query(&params, &params);
93     DBG(cout << "MFX Query: " << res << endl << params.mfx << params.mfx.FrameInfo);
94     CV_Assert(res >= MFX_ERR_NONE);
95
96     // Init surface pool
97
98     pool = SurfacePool::create(decoder, params);
99     if (!pool)
100     {
101         MSG(cerr << "MFX: Failed to create surface pool" << endl);
102         return;
103     }
104
105     // Init decoder
106
107     res = decoder->Init(&params);
108     DBG(cout << "MFX Init: " << res << endl << params.mfx.FrameInfo);
109     if (res < MFX_ERR_NONE)
110     {
111         MSG(cerr << "MFX: Failed to init decoder: " << res << endl);
112         return;
113     }
114
115     frameSize = Size(params.mfx.FrameInfo.CropW, params.mfx.FrameInfo.CropH);
116     good = true;
117 }
118
119
120 VideoCapture_IntelMFX::~VideoCapture_IntelMFX()
121 {
122     cleanup(plugin);
123     cleanup(bs);
124     cleanup(decoder);
125     cleanup(pool);
126     session->Close();
127     cleanup(session);
128     cleanup(deviceHandler);
129 }
130
131 double VideoCapture_IntelMFX::getProperty(int prop) const
132 {
133     if (!good)
134     {
135         MSG(cerr << "MFX: can not call getProperty(), backend has not been initialized" << endl);
136         return 0;
137     }
138     switch (prop)
139     {
140         case CAP_PROP_FRAME_WIDTH:
141             return frameSize.width;
142         case CAP_PROP_FRAME_HEIGHT:
143             return frameSize.height;
144         default:
145             MSG(cerr << "MFX: unsupported property" << endl);
146             return 0;
147     }
148 }
149
150 bool VideoCapture_IntelMFX::setProperty(int, double)
151 {
152     MSG(cerr << "MFX: setProperty() is not implemented" << endl);
153     return false;
154 }
155
156 bool VideoCapture_IntelMFX::grabFrame()
157 {
158     mfxStatus res;
159     mfxFrameSurface1 *workSurface = 0;
160     mfxSyncPoint sync;
161
162     workSurface = pool->getFreeSurface();
163
164     while (true)
165     {
166         if (!workSurface)
167         {
168             // not enough surfaces
169             MSG(cerr << "MFX: Failed to get free surface" << endl);
170             return false;
171         }
172
173         outSurface = 0;
174         res = decoder->DecodeFrameAsync(bs->drain ? 0 : &bs->stream, workSurface, (mfxFrameSurface1**)&outSurface, &sync);
175         if (res == MFX_ERR_NONE)
176         {
177             res = session->SyncOperation(sync, 1000); // 1 sec, TODO: provide interface to modify timeout
178             if (res == MFX_ERR_NONE)
179             {
180                 // ready to retrieve
181                 DBG(cout << "Frame ready to retrieve" << endl);
182                 return true;
183             }
184             else
185             {
186                 MSG(cerr << "MFX: Sync error: " << res << endl);
187                 return false;
188             }
189         }
190         else if (res == MFX_ERR_MORE_DATA)
191         {
192             if (bs->isDone())
193             {
194                 if (bs->drain)
195                 {
196                     // finish
197                     DBG(cout << "Drain finished" << endl);
198                     return false;
199                 }
200                 else
201                 {
202                     DBG(cout << "Bitstream finished - Drain started" << endl);
203                     bs->drain = true;
204                     continue;
205                 }
206             }
207             else
208             {
209                 bool read_res = bs->read();
210                 if (!read_res)
211                 {
212                     // failed to read
213                     MSG(cerr << "MFX: Bitstream read failure" << endl);
214                     return false;
215                 }
216                 else
217                 {
218                     DBG(cout << "Bitstream read success" << endl);
219                     continue;
220                 }
221             }
222         }
223         else if (res == MFX_ERR_MORE_SURFACE)
224         {
225             DBG(cout << "Getting another surface" << endl);
226             workSurface = pool->getFreeSurface();
227             continue;
228         }
229         else if (res == MFX_WRN_DEVICE_BUSY)
230         {
231             DBG(cout << "Waiting for device" << endl);
232             sleep_ms(1000);
233             continue;
234         }
235         else if (res == MFX_WRN_VIDEO_PARAM_CHANGED)
236         {
237             DBG(cout << "Video param changed" << endl);
238             continue;
239         }
240         else
241         {
242             MSG(cerr << "MFX: Bad status: " << res << endl);
243             return false;
244         }
245     }
246 }
247
248
249 bool VideoCapture_IntelMFX::retrieveFrame(int, OutputArray out)
250 {
251     if (!outSurface)
252     {
253         MSG(cerr << "MFX: No frame ready to retrieve" << endl);
254         return false;
255     }
256     mfxFrameSurface1 * s = (mfxFrameSurface1*)outSurface;
257     mfxFrameInfo &info = s->Info;
258     mfxFrameData &data = s->Data;
259
260     const int cols = info.CropW;
261     const int rows = info.CropH;
262
263     out.create(rows, cols, CV_8UC3);
264     Mat res = out.getMat();
265
266     hal::cvtTwoPlaneYUVtoBGR(data.Y, data.UV, data.Pitch, res.data, res.step, cols, rows, 3, false, 0);
267
268     return true;
269 }
270
271 bool VideoCapture_IntelMFX::isOpened() const
272 {
273     return good;
274 }
275
276 int VideoCapture_IntelMFX::getCaptureDomain()
277 {
278     return CAP_INTEL_MFX;
279 }
280
281 //==================================================================================================
282
283 cv::Ptr<IVideoCapture> cv::create_MFX_capture(const std::string &filename)
284 {
285     return cv::makePtr<VideoCapture_IntelMFX>(filename);
286 }