MythTV  master
dxva2decoder.cpp
Go to the documentation of this file.
1 #include <QString>
2 #include <QLibrary>
3 
5 #include "initguid.h"
7 #include "dxva2decoder.h"
8 
9 #ifndef __MINGW32__
11 {
12  0xfc51a551, 0xd5e7, 0x11d9, {0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02}
13 };
14 #endif
15 
16 static inline QString toString(const GUID& guid)
17 {
18  return QString("%1-%2-%3-%4%5-%6%7%8%9%10%11")
19  .arg(guid.Data1, 8, 16, QLatin1Char('0'))
20  .arg(guid.Data2, 4, 16, QLatin1Char('0'))
21  .arg(guid.Data3, 4, 16, QLatin1Char('0'))
22  .arg(guid.Data4[0], 2, 16, QLatin1Char('0'))
23  .arg(guid.Data4[1], 2, 16, QLatin1Char('0'))
24  .arg(guid.Data4[2], 2, 16, QLatin1Char('0'))
25  .arg(guid.Data4[3], 2, 16, QLatin1Char('0'))
26  .arg(guid.Data4[4], 2, 16, QLatin1Char('0'))
27  .arg(guid.Data4[5], 2, 16, QLatin1Char('0'))
28  .arg(guid.Data4[6], 2, 16, QLatin1Char('0'))
29  .arg(guid.Data4[7], 2, 16, QLatin1Char('0')).toUpper();
30 }
31 
32 #define LOC QString("DXVA2: ")
33 #define ERR QString("DXVA2 Error: ")
34 
35 #ifndef __MINGW32__
36 DEFINE_GUID(DXVA2_ModeH264_A, 0x1b81be64, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
37 DEFINE_GUID(DXVA2_ModeH264_B, 0x1b81be65, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
38 DEFINE_GUID(DXVA2_ModeH264_C, 0x1b81be66, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
39 DEFINE_GUID(DXVA2_ModeH264_D, 0x1b81be67, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
40 DEFINE_GUID(DXVA2_ModeH264_E, 0x1b81be68, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
41 DEFINE_GUID(DXVA2_ModeH264_F, 0x1b81be69, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
42 
43 DEFINE_GUID(DXVA2_ModeWMV8_A, 0x1b81be80, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
44 DEFINE_GUID(DXVA2_ModeWMV8_B, 0x1b81be81, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
45 
46 DEFINE_GUID(DXVA2_ModeWMV9_A, 0x1b81be90, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
47 DEFINE_GUID(DXVA2_ModeWMV9_B, 0x1b81be91, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
48 DEFINE_GUID(DXVA2_ModeWMV9_C, 0x1b81be94, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
49 
50 DEFINE_GUID(DXVA2_ModeVC1_A, 0x1b81beA0, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
51 DEFINE_GUID(DXVA2_ModeVC1_B, 0x1b81beA1, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
52 DEFINE_GUID(DXVA2_ModeVC1_C, 0x1b81beA2, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
53 DEFINE_GUID(DXVA2_ModeVC1_D, 0x1b81beA3, 0xa0c7, 0x11d3, 0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
54 
55 DEFINE_GUID(DXVA2_ModeMPEG2_MoComp, 0xe6a9f44b, 0x61b0, 0x4563, 0x9e,0xa4,0x63,0xd2,0xa3,0xc6,0xfe,0x66);
56 DEFINE_GUID(DXVA2_ModeMPEG2_IDCT, 0xbf22ad00, 0x03ea, 0x4690, 0x80,0x77,0x47,0x33,0x46,0x20,0x9b,0x7e);
57 DEFINE_GUID(DXVA2_ModeMPEG2_VLD, 0xee27417f, 0x5e28, 0x4e65, 0xbe,0xea,0x1d,0x26,0xb5,0x08,0xad,0xc9);
58 #endif
59 
60 #define DXVA2_ModeWMV8_PostProc DXVA2_ModeWMV8_A
61 #define DXVA2_ModeWMV8_MoComp DXVA2_ModeWMV8_B
62 
63 #define DXVA2_ModeWMV9_PostProc DXVA2_ModeWMV9_A
64 #define DXVA2_ModeWMV9_MoComp DXVA2_ModeWMV9_B
65 #define DXVA2_ModeWMV9_IDCT DXVA2_ModeWMV9_C
66 
67 #define DXVA2_ModeVC1_PostProc DXVA2_ModeVC1_A
68 #define DXVA2_ModeVC1_MoComp DXVA2_ModeVC1_B
69 #define DXVA2_ModeVC1_IDCT DXVA2_ModeVC1_C
70 #define DXVA2_ModeVC1_VLD DXVA2_ModeVC1_D
71 
72 #define DXVA2_ModeH264_MoComp_NoFGT DXVA2_ModeH264_A
73 #define DXVA2_ModeH264_MoComp_FGT DXVA2_ModeH264_B
74 #define DXVA2_ModeH264_IDCT_NoFGT DXVA2_ModeH264_C
75 #define DXVA2_ModeH264_IDCT_FGT DXVA2_ModeH264_D
76 #define DXVA2_ModeH264_VLD_NoFGT DXVA2_ModeH264_E
77 #define DXVA2_ModeH264_VLD_FGT DXVA2_ModeH264_F
78 
79 DEFINE_GUID(DXVA2_Intel_ModeH264_A, 0x604F8E64, 0x4951, 0x4c54, 0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6);
80 DEFINE_GUID(DXVA2_Intel_ModeH264_C, 0x604F8E66, 0x4951, 0x4c54, 0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6);
81 DEFINE_GUID(DXVA2_Intel_ModeH264_E, 0x604F8E68, 0x4951, 0x4c54, 0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6);
82 DEFINE_GUID(DXVA2_Intel_ModeVC1_E , 0xBCC5DB6D, 0xA2B6, 0x4AF0, 0xAC,0xE4,0xAD,0xB1,0xF7,0x87,0xBC,0x89);
83 
84 struct dxva2_mode {
85  const QString name;
86  const GUID *guid;
88 };
89 
90 static const dxva2_mode dxva2_modes[] =
91 {
92  {"MPEG2 VLD", &DXVA2_ModeMPEG2_VLD, kCodec_MPEG2_DXVA2},
93  {"MPEG2 MoComp", &DXVA2_ModeMPEG2_MoComp, kCodec_NONE},
94  {"MPEG2 IDCT", &DXVA2_ModeMPEG2_IDCT, kCodec_NONE},
95 
96  {"H.264 VLD, FGT", &DXVA2_ModeH264_F, kCodec_H264_DXVA2},
97  {"H.264 VLD, no FGT", &DXVA2_ModeH264_E, kCodec_H264_DXVA2},
98  {"H.264 IDCT, FGT", &DXVA2_ModeH264_D, kCodec_NONE},
99  {"H.264 IDCT, no FGT", &DXVA2_ModeH264_C, kCodec_NONE},
100  {"H.264 MoComp, FGT", &DXVA2_ModeH264_B, kCodec_NONE},
101  {"H.264 MoComp, no FGT", &DXVA2_ModeH264_A, kCodec_NONE},
102 
103  {"WMV8 MoComp", &DXVA2_ModeWMV8_B, kCodec_NONE},
104  {"WMV8 post processing", &DXVA2_ModeWMV8_A, kCodec_NONE},
105 
106  {"WMV9 IDCT", &DXVA2_ModeWMV9_C, kCodec_NONE},
107  {"WMV9 MoComp", &DXVA2_ModeWMV9_B, kCodec_NONE},
108  {"WMV9 post processing", &DXVA2_ModeWMV9_A, kCodec_NONE},
109 
110  {"VC-1 VLD", &DXVA2_ModeVC1_D, kCodec_VC1_DXVA2},
111  {"VC-1 VLD", &DXVA2_ModeVC1_D, kCodec_WMV3_DXVA2},
112  {"VC-1 IDCT", &DXVA2_ModeVC1_C, kCodec_NONE},
113  {"VC-1 MoComp", &DXVA2_ModeVC1_B, kCodec_NONE},
114  {"VC-1 post processing", &DXVA2_ModeVC1_A, kCodec_NONE},
115 
116  {"Intel H.264 VLD, no FGT", &DXVA2_Intel_ModeH264_E, kCodec_H264_DXVA2},
117  {"Intel H.264 IDCT, no FGT", &DXVA2_Intel_ModeH264_C, kCodec_NONE},
118  {"Intel H.264 MoComp, no FGT", &DXVA2_Intel_ModeH264_A, kCodec_NONE},
119  {"Intel VC-1 VLD", &DXVA2_Intel_ModeVC1_E, kCodec_VC1_DXVA2},
120 
121  {"", nullptr, kCodec_NONE}
122 };
123 
124 #define CREATE_CHECK(arg1, arg2) \
125  if (ok) \
126  { \
127  ok = arg1; \
128  if (!ok) \
129  LOG(VB_GENERAL, LOG_ERR, LOC + (arg2)); \
130  }
131 
133  uint width, uint height)
134  : m_codec_id(codec_id), m_width(width), m_height(height)
135 {
136  memset(&m_format, 0, sizeof(DXVA2_VideoDesc));
137  memset(&m_context, 0, sizeof(dxva_context));
138  memset(&m_config, 0, sizeof(DXVA2_ConfigPictureDecode));
139  m_context.cfg = &m_config;
140  m_context.surface_count = num_bufs;
141  m_context.surface = new IDirect3DSurface9*[num_bufs];
142  for (uint i = 0; i < num_bufs; i++)
143  m_context.surface[i] = nullptr;
144 }
145 
147 {
148  DestroyDecoder();
149  DestroySurfaces();
151 }
152 
154 {
155  bool ok = true;
156  CREATE_CHECK(m_width > 0, "Invalid width.")
157  CREATE_CHECK(m_height > 0, "Invalid height.")
158  CREATE_CHECK(CreateVideoService(render), "Failed to create video service.")
159  CREATE_CHECK(GetInputOutput(), "Failed to find input/output combination.")
160  InitFormat();
161  CREATE_CHECK(GetDecoderConfig(), "Failed to find a raw input bitstream.")
162  CREATE_CHECK(CreateSurfaces(), "Failed to create surfaces.")
163  CREATE_CHECK(CreateDecoder(), "Failed to create decoder.")
164  return ok;
165 }
166 
167 using DXVA2CreateVideoServicePtr = HRESULT (__stdcall *)(IDirect3DDevice9* pDD,
168  REFIID riid,
169  void** ppService);
170 
172 {
173  // Loads the DXVA2 library
174  DXVA2CreateVideoServicePtr create_video_service =
175  (DXVA2CreateVideoServicePtr)MythRenderD3D9::ResolveAddress("DXVA2", "DXVA2CreateVideoService");
176  if (!create_video_service)
177  return false;
178 
179  m_deviceManager = render->GetDeviceManager();
180  if (!m_deviceManager)
181  {
182  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to get device manager.");
183  return false;
184  }
185 
186  HRESULT hr = IDirect3DDeviceManager9_OpenDeviceHandle(m_deviceManager, &m_device);
187  if (FAILED(hr))
188  {
189  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to get device handle.");
190  return false;
191  }
192 
193  hr = IDirect3DDeviceManager9_GetVideoService(m_deviceManager,
194  m_device,
196  (void**)&m_service);
197  if (FAILED(hr))
198  {
199  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to get video service.");
200  return false;
201  }
202 
203  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Created DXVA2 video service.");
204  return true;
205 }
206 
208 {
209  if (m_device)
210  IDirect3DDeviceManager9_CloseDeviceHandle(m_deviceManager, m_device);
211  if (m_service)
212  IDirectXVideoDecoderService_Release(m_service);
213  m_deviceManager = nullptr;
214  m_device = nullptr;
215  m_service = nullptr;
216 }
217 
219 {
220  if (!m_service)
221  return false;
222  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Looking for support for %1")
223  .arg(toString(m_codec_id)));
224  uint input_count;
225  GUID *input_list;
226  m_format.Format = D3DFMT_UNKNOWN;
227  IDirectXVideoDecoderService_GetDecoderDeviceGuids(
228  m_service, &input_count, &input_list);
229 
230  for (const dxva2_mode* mode = dxva2_modes;
231  !mode->name.isEmpty() && m_format.Format == D3DFMT_UNKNOWN;
232  mode++)
233  {
234  if (mode->codec != m_codec_id)
235  continue;
236  for (uint j = 0; j < input_count; j++)
237  {
238  if (IsEqualGUID(input_list[j], *mode->guid))
239  {
240  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Testing %1")
241  .arg(mode->name));
242  if (TestTarget(input_list[j]))
243  break;
244  }
245  }
246  }
247 
248  return m_format.Format != D3DFMT_UNKNOWN;
249 }
250 
251 bool DXVA2Decoder::TestTarget(const GUID &guid)
252 {
253  if (!m_service)
254  return false;
255  uint output_count = 0;
256  D3DFORMAT *output_list = nullptr;
257  IDirectXVideoDecoderService_GetDecoderRenderTargets(
258  m_service, guid, &output_count, &output_list);
259  for(uint i = 0; i < output_count; i++)
260  {
261  if(output_list[i] == MAKEFOURCC('Y','V','1','2') ||
262  output_list[i] == MAKEFOURCC('N','V','1','2'))
263  {
264  m_input = guid;
265  m_format.Format = output_list[i];
266  return true;
267  }
268  }
269  return false;
270 }
271 
273 {
274  m_format.SampleWidth = m_width;
275  m_format.SampleHeight = m_height;
276  m_format.InputSampleFreq.Numerator = 0;
277  m_format.InputSampleFreq.Denominator = 0;
278  m_format.OutputFrameFreq = m_format.InputSampleFreq;
279  m_format.UABProtectionLevel = false;
280  m_format.Reserved = 0;
281 }
282 
284 {
285  if (!m_service)
286  return false;
287 
288  uint cfg_count = 0;
289  DXVA2_ConfigPictureDecode *cfg_list = nullptr;
290  IDirectXVideoDecoderService_GetDecoderConfigurations(
291  m_service, m_input, &m_format, nullptr, &cfg_count, &cfg_list);
292 
293  DXVA2_ConfigPictureDecode config = {};
294  uint bitstream = 1;
295  for (uint i = 0; i < cfg_count; i++)
296  {
297  // select first available
298  if (config.ConfigBitstreamRaw == 0 &&
299  cfg_list[i].ConfigBitstreamRaw != 0)
300  {
301  config = cfg_list[i];
302  }
303  // overide with preferred if found
304  if (config.ConfigBitstreamRaw != bitstream &&
305  cfg_list[i].ConfigBitstreamRaw == bitstream)
306  {
307  config = cfg_list[i];
308  }
309  }
310  //CoTaskMemFree(cfg_list);
311  if(!config.ConfigBitstreamRaw)
312  return false;
313  m_config = config;
314  //*const_cast<DXVA2_ConfigPictureDecode*>(m_context.cfg) = config;
315  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Found bitstream type %1")
316  .arg(m_context.cfg->ConfigBitstreamRaw));
317  return true;
318 }
319 
321 {
322  if (!m_service || !m_context.surface)
323  return false;
324 
325  HRESULT hr = IDirectXVideoDecoderService_CreateSurface(
326  m_service, (m_width + 15) & ~15, (m_height + 15) & ~15,
327  m_context.surface_count - 1, m_format.Format,
328  D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget,
329  m_context.surface, nullptr);
330 
331  if (FAILED(hr))
332  return false;
333 
334  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created %1 decoder surfaces.")
335  .arg(m_context.surface_count));
336 
337  return true;
338 }
339 
341 {
342  for (uint i = 0; i < m_context.surface_count; i++)
343  {
344  if (m_context.surface[i])
345  {
346  m_context.surface[i]->Release();
347  m_context.surface[i] = nullptr;
348  }
349  }
350  m_context.surface = nullptr;
351  m_context.surface_count = 0;
352 }
353 
355 {
356  if (!m_service || !m_context.surface)
357  return false;
358 
359  HRESULT hr = IDirectXVideoDecoderService_CreateVideoDecoder(
361  const_cast<DXVA2_ConfigPictureDecode*>(m_context.cfg),
362  m_context.surface, m_context.surface_count, &m_context.decoder);
363 
364  if (FAILED(hr))
365  return false;
366 
367  LOG(VB_PLAYBACK, LOG_INFO, LOC +
368  QString("Created decoder: %1->%2x%3 (%4 surfaces)")
369  .arg(toString(m_codec_id)).arg(m_width).arg(m_height)
370  .arg(m_context.surface_count));
371  return true;
372 }
373 
375 {
376  if (m_context.decoder)
377  IDirectXVideoDecoder_Release(m_context.decoder);
378  m_context.decoder = nullptr;
379 }
380 
382 {
383  if (num >= m_context.surface_count)
384  return nullptr;
385  return (void*)m_context.surface[num];
386 }
DXVA2Decoder::Init
bool Init(MythRenderD3D9 *render)
Definition: dxva2decoder.cpp:153
DXVA2CreateVideoServicePtr
HRESULT(__stdcall *)(IDirect3DDevice9 *pDD, REFIID riid, void **ppService) DXVA2CreateVideoServicePtr
Definition: dxva2decoder.cpp:169
MythRenderD3D9::GetDeviceManager
IDirect3DDeviceManager9 * GetDeviceManager(void)
Definition: mythrender_d3d9.h:167
kCodec_NONE
@ kCodec_NONE
Definition: mythcodecid.h:14
dxva2_mode::guid
const GUID * guid
Definition: dxva2decoder.cpp:86
kCodec_VC1_DXVA2
@ kCodec_VC1_DXVA2
Definition: mythcodecid.h:106
DXVA2Decoder::m_input
GUID m_input
Definition: dxva2decoder.h:40
DXVA2Decoder::m_codec_id
MythCodecID m_codec_id
Definition: dxva2decoder.h:39
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
dxva2decoder.h
dxva2_mode::codec
MythCodecID codec
Definition: dxva2decoder.cpp:87
dxva2_mode::name
const QString name
Definition: dxva2decoder.cpp:85
MythCodecID
MythCodecID
Definition: mythcodecid.h:10
dxva2_modes
static const dxva2_mode dxva2_modes[]
Definition: dxva2decoder.cpp:90
mythlogging.h
DXVA2Decoder::m_context
struct dxva_context m_context
Definition: dxva2decoder.h:37
DXVA2Decoder::InitFormat
void InitFormat(void)
Definition: dxva2decoder.cpp:272
CREATE_CHECK
#define CREATE_CHECK(arg1, arg2)
Definition: dxva2decoder.cpp:124
DXVA2Decoder::GetInputOutput
bool GetInputOutput(void)
Definition: dxva2decoder.cpp:218
DXVA2Decoder::GetDecoderConfig
bool GetDecoderConfig(void)
Definition: dxva2decoder.cpp:283
DXVA2Decoder::m_deviceManager
IDirect3DDeviceManager9 * m_deviceManager
Definition: dxva2decoder.h:34
DXVA2Decoder::m_config
DXVA2_ConfigPictureDecode m_config
Definition: dxva2decoder.h:38
MythRenderD3D9
Definition: mythrender_d3d9.h:91
dxva2_mode
Definition: dxva2decoder.cpp:84
DXVA2Decoder::DXVA2Decoder
DXVA2Decoder(uint num_bufs, MythCodecID codec_id, uint width, uint height)
Definition: dxva2decoder.cpp:132
DXVA2Decoder::CreateSurfaces
bool CreateSurfaces(void)
Definition: dxva2decoder.cpp:320
uint
unsigned int uint
Definition: compat.h:81
MythRenderD3D9::ResolveAddress
static void * ResolveAddress(const char *lib, const char *proc)
Definition: mythrender_d3d9.cpp:152
DEFINE_GUID
DEFINE_GUID(DXVA2_ModeH264_A, 0x1b81be64, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5)
LOC
#define LOC
Definition: dxva2decoder.cpp:32
IID_IDirectXVideoDecoderService
static const GUID IID_IDirectXVideoDecoderService
Definition: dxva2decoder.cpp:10
DXVA2Decoder::DestroyVideoService
void DestroyVideoService(void)
Definition: dxva2decoder.cpp:207
DXVA2Decoder::m_width
uint m_width
Definition: dxva2decoder.h:42
kCodec_H264_DXVA2
@ kCodec_H264_DXVA2
Definition: mythcodecid.h:105
DXVA2Decoder::m_device
HANDLE m_device
Definition: dxva2decoder.h:35
DXVA2Decoder::DestroyDecoder
void DestroyDecoder(void)
Definition: dxva2decoder.cpp:374
DXVA2Decoder::~DXVA2Decoder
~DXVA2Decoder(void)
Definition: dxva2decoder.cpp:146
DXVA2Decoder::m_format
DXVA2_VideoDesc m_format
Definition: dxva2decoder.h:41
mythrender_d3d9.h
DXVA2Decoder::CreateDecoder
bool CreateDecoder(void)
Definition: dxva2decoder.cpp:354
kCodec_WMV3_DXVA2
@ kCodec_WMV3_DXVA2
Definition: mythcodecid.h:107
DXVA2Decoder::DestroySurfaces
void DestroySurfaces(void)
Definition: dxva2decoder.cpp:340
DXVA2Decoder::GetSurface
void * GetSurface(uint num)
Definition: dxva2decoder.cpp:381
DXVA2Decoder::TestTarget
bool TestTarget(const GUID &guid)
Definition: dxva2decoder.cpp:251
kCodec_MPEG2_DXVA2
@ kCodec_MPEG2_DXVA2
Definition: mythcodecid.h:102
toString
static QString toString(const GUID &guid)
Definition: dxva2decoder.cpp:16
DXVA2Decoder::m_service
IDirectXVideoDecoderService * m_service
Definition: dxva2decoder.h:36
DXVA2Decoder::m_height
uint m_height
Definition: dxva2decoder.h:43
DXVA2Decoder::CreateVideoService
bool CreateVideoService(MythRenderD3D9 *render)
Definition: dxva2decoder.cpp:171