1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "DVDVideoCodecFFmpeg.h"
10
11 #include "DVDCodecs/DVDCodecs.h"
12 #include "DVDCodecs/DVDFactoryCodec.h"
13 #include "DVDStreamInfo.h"
14 #include "ServiceBroker.h"
15 #include "cores/VideoPlayer/Interface/TimingConstants.h"
16 #include "cores/VideoPlayer/VideoRenderers/RenderManager.h"
17 #include "cores/VideoSettings.h"
18 #include "settings/AdvancedSettings.h"
19 #include "settings/Settings.h"
20 #include "settings/SettingsComponent.h"
21 #include "utils/CPUInfo.h"
22 #include "utils/StringUtils.h"
23 #include "utils/XTimeUtils.h"
24 #include "utils/log.h"
25
26 #include <memory>
27
28 #include "system.h"
29
30 extern "C" {
31 #include <libavutil/opt.h>
32 #include <libavutil/mastering_display_metadata.h>
33 #include <libavfilter/avfilter.h>
34 #include <libavfilter/buffersink.h>
35 #include <libavfilter/buffersrc.h>
36 #include <libavutil/pixdesc.h>
37 }
38
39 #ifndef TARGET_POSIX
40 #define RINT(x) ((x) >= 0 ? ((int)((x) + 0.5)) : ((int)((x) - 0.5)))
41 #else
42 #include <math.h>
43 #define RINT lrint
44 #endif
45
46 enum DecoderState
47 {
48 STATE_NONE,
49 STATE_SW_SINGLE,
50 STATE_HW_SINGLE,
51 STATE_HW_FAILED,
52 STATE_SW_MULTI
53 };
54
55 enum EFilterFlags {
56 FILTER_NONE = 0x0,
57 FILTER_DEINTERLACE_YADIF = 0x1, //< use first deinterlace mode
58 FILTER_DEINTERLACE_ANY = 0xf, //< use any deinterlace mode
59 FILTER_DEINTERLACE_FLAGGED = 0x10, //< only deinterlace flagged frames
60 FILTER_DEINTERLACE_HALFED = 0x20, //< do half rate deinterlacing
61 FILTER_ROTATE = 0x40, //< rotate image according to the codec hints
62 };
63
64 //------------------------------------------------------------------------------
65 // Video Buffers
66 //------------------------------------------------------------------------------
67
68 class CVideoBufferFFmpeg : public CVideoBuffer
69 {
70 public:
71 CVideoBufferFFmpeg(IVideoBufferPool &pool, int id);
72 ~CVideoBufferFFmpeg() override;
73 void GetPlanes(uint8_t*(&planes)[YuvImage::MAX_PLANES]) override;
74 void GetStrides(int(&strides)[YuvImage::MAX_PLANES]) override;
75
76 void SetRef(AVFrame *frame);
77 void Unref();
78
79 protected:
80 AVFrame* m_pFrame;
81 };
82
CVideoBufferFFmpeg(IVideoBufferPool & pool,int id)83 CVideoBufferFFmpeg::CVideoBufferFFmpeg(IVideoBufferPool &pool, int id)
84 : CVideoBuffer(id)
85 {
86 m_pFrame = av_frame_alloc();
87 }
88
~CVideoBufferFFmpeg()89 CVideoBufferFFmpeg::~CVideoBufferFFmpeg()
90 {
91 av_frame_free(&m_pFrame);
92 }
93
GetPlanes(uint8_t * (& planes)[YuvImage::MAX_PLANES])94 void CVideoBufferFFmpeg::GetPlanes(uint8_t*(&planes)[YuvImage::MAX_PLANES])
95 {
96 planes[0] = m_pFrame->data[0];
97 planes[1] = m_pFrame->data[1];
98 planes[2] = m_pFrame->data[2];
99 }
100
GetStrides(int (& strides)[YuvImage::MAX_PLANES])101 void CVideoBufferFFmpeg::GetStrides(int(&strides)[YuvImage::MAX_PLANES])
102 {
103 strides[0] = m_pFrame->linesize[0];
104 strides[1] = m_pFrame->linesize[1];
105 strides[2] = m_pFrame->linesize[2];
106 }
107
SetRef(AVFrame * frame)108 void CVideoBufferFFmpeg::SetRef(AVFrame *frame)
109 {
110 av_frame_unref(m_pFrame);
111 av_frame_move_ref(m_pFrame, frame);
112 m_pixFormat = (AVPixelFormat)m_pFrame->format;
113 }
114
Unref()115 void CVideoBufferFFmpeg::Unref()
116 {
117 av_frame_unref(m_pFrame);
118 }
119
120 //------------------------------------------------------------------------------
121
122 class CVideoBufferPoolFFmpeg : public IVideoBufferPool
123 {
124 public:
125 ~CVideoBufferPoolFFmpeg() override;
126 void Return(int id) override;
127 CVideoBuffer* Get() override;
128
129 protected:
130 CCriticalSection m_critSection;
131 std::vector<CVideoBufferFFmpeg*> m_all;
132 std::deque<int> m_used;
133 std::deque<int> m_free;
134 };
135
~CVideoBufferPoolFFmpeg()136 CVideoBufferPoolFFmpeg::~CVideoBufferPoolFFmpeg()
137 {
138 for (auto buf : m_all)
139 {
140 delete buf;
141 }
142 }
143
Get()144 CVideoBuffer* CVideoBufferPoolFFmpeg::Get()
145 {
146 CSingleLock lock(m_critSection);
147
148 CVideoBufferFFmpeg *buf = nullptr;
149 if (!m_free.empty())
150 {
151 int idx = m_free.front();
152 m_free.pop_front();
153 m_used.push_back(idx);
154 buf = m_all[idx];
155 }
156 else
157 {
158 int id = m_all.size();
159 buf = new CVideoBufferFFmpeg(*this, id);
160 m_all.push_back(buf);
161 m_used.push_back(id);
162 }
163
164 buf->Acquire(GetPtr());
165 return buf;
166 }
167
Return(int id)168 void CVideoBufferPoolFFmpeg::Return(int id)
169 {
170 CSingleLock lock(m_critSection);
171
172 m_all[id]->Unref();
173 auto it = m_used.begin();
174 while (it != m_used.end())
175 {
176 if (*it == id)
177 {
178 m_used.erase(it);
179 break;
180 }
181 else
182 ++it;
183 }
184 m_free.push_back(id);
185 }
186
187 //------------------------------------------------------------------------------
188 // main class
189 //------------------------------------------------------------------------------
190
CDropControl()191 CDVDVideoCodecFFmpeg::CDropControl::CDropControl()
192 {
193 Reset(true);
194 }
195
Reset(bool init)196 void CDVDVideoCodecFFmpeg::CDropControl::Reset(bool init)
197 {
198 m_lastPTS = AV_NOPTS_VALUE;
199
200 if (init || m_state != VALID)
201 {
202 m_count = 0;
203 m_diffPTS = 0;
204 m_state = INIT;
205 }
206 }
207
Process(int64_t pts,bool drop)208 void CDVDVideoCodecFFmpeg::CDropControl::Process(int64_t pts, bool drop)
209 {
210 if (m_state == INIT)
211 {
212 if (pts != AV_NOPTS_VALUE && m_lastPTS != AV_NOPTS_VALUE)
213 {
214 m_diffPTS += pts - m_lastPTS;
215 m_count++;
216 }
217 if (m_count > 10)
218 {
219 m_diffPTS = m_diffPTS / m_count;
220 if (m_diffPTS > 0)
221 {
222 CLog::Log(LOGINFO, "CDVDVideoCodecFFmpeg::CDropControl: calculated diff time: %" PRId64,
223 m_diffPTS);
224 m_state = CDropControl::VALID;
225 m_count = 0;
226 }
227 }
228 }
229 else if (m_state == VALID && !drop)
230 {
231 if (std::abs(pts - m_lastPTS - m_diffPTS) > m_diffPTS * 0.2)
232 {
233 m_count++;
234 if (m_count > 5)
235 {
236 CLog::Log(LOGINFO, "CDVDVideoCodecFFmpeg::CDropControl: lost diff");
237 Reset(true);
238 }
239 }
240 else
241 m_count = 0;
242 }
243 m_lastPTS = pts;
244 }
245
GetFormat(struct AVCodecContext * avctx,const AVPixelFormat * fmt)246 enum AVPixelFormat CDVDVideoCodecFFmpeg::GetFormat(struct AVCodecContext * avctx, const AVPixelFormat * fmt)
247 {
248 ICallbackHWAccel *cb = static_cast<ICallbackHWAccel*>(avctx->opaque);
249 CDVDVideoCodecFFmpeg* ctx = dynamic_cast<CDVDVideoCodecFFmpeg*>(cb);
250
251 const char* pixFmtName = av_get_pix_fmt_name(*fmt);
252
253 ctx->m_processInfo.SetVideoDimensions(avctx->coded_width, avctx->coded_height);
254
255 // if frame threading is enabled hw accel is not allowed
256 // 2nd condition:
257 // fix an ffmpeg issue here, it calls us with an invalid profile
258 // then a 2nd call with a valid one
259 if(ctx->m_decoderState != STATE_HW_SINGLE ||
260 (avctx->codec_id == AV_CODEC_ID_VC1 && avctx->profile == FF_PROFILE_UNKNOWN))
261 {
262 AVPixelFormat defaultFmt = avcodec_default_get_format(avctx, fmt);
263 pixFmtName = av_get_pix_fmt_name(defaultFmt);
264 ctx->m_processInfo.SetVideoPixelFormat(pixFmtName ? pixFmtName : "");
265 ctx->m_processInfo.SetSwDeinterlacingMethods();
266 return defaultFmt;
267 }
268
269 // hardware decoder de-selected, restore standard ffmpeg
270 if (ctx->HasHardware())
271 {
272 ctx->SetHardware(nullptr);
273 avctx->get_buffer2 = avcodec_default_get_buffer2;
274 avctx->slice_flags = 0;
275 av_buffer_unref(&avctx->hw_frames_ctx);
276 }
277
278 const AVPixelFormat * cur = fmt;
279 while (*cur != AV_PIX_FMT_NONE)
280 {
281 pixFmtName = av_get_pix_fmt_name(*cur);
282
283 auto hwaccels = CDVDFactoryCodec::GetHWAccels();
284 for (auto &hwaccel : hwaccels)
285 {
286 IHardwareDecoder *pDecoder(CDVDFactoryCodec::CreateVideoCodecHWAccel(hwaccel, ctx->m_hints,
287 ctx->m_processInfo, *cur));
288 if (pDecoder)
289 {
290 if (pDecoder->Open(avctx, ctx->m_pCodecContext, *cur))
291 {
292 ctx->m_processInfo.SetVideoPixelFormat(pixFmtName ? pixFmtName : "");
293 ctx->SetHardware(pDecoder);
294 return *cur;
295 }
296 }
297 SAFE_RELEASE(pDecoder);
298 }
299 cur++;
300 }
301
302 ctx->m_processInfo.SetVideoPixelFormat(pixFmtName ? pixFmtName : "");
303 ctx->m_decoderState = STATE_HW_FAILED;
304 return avcodec_default_get_format(avctx, fmt);
305 }
306
CDVDVideoCodecFFmpeg(CProcessInfo & processInfo)307 CDVDVideoCodecFFmpeg::CDVDVideoCodecFFmpeg(CProcessInfo &processInfo)
308 : CDVDVideoCodec(processInfo), m_postProc(processInfo)
309 {
310 m_videoBufferPool = std::make_shared<CVideoBufferPoolFFmpeg>();
311
312 m_decoderState = STATE_NONE;
313 }
314
~CDVDVideoCodecFFmpeg()315 CDVDVideoCodecFFmpeg::~CDVDVideoCodecFFmpeg()
316 {
317 Dispose();
318 }
319
Open(CDVDStreamInfo & hints,CDVDCodecOptions & options)320 bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
321 {
322 if (hints.cryptoSession)
323 {
324 CLog::Log(LOGERROR,"CDVDVideoCodecFFmpeg::Open() CryptoSessions unsupported!");
325 return false;
326 }
327
328 m_hints = hints;
329 m_options = options;
330
331 AVCodec* pCodec;
332
333 m_iOrientation = hints.orientation;
334
335 m_formats.clear();
336 m_formats = m_processInfo.GetPixFormats();
337 m_formats.push_back(AV_PIX_FMT_NONE); /* always add none to get a terminated list in ffmpeg world */
338 m_processInfo.SetSwDeinterlacingMethods();
339 m_processInfo.SetVideoInterlaced(false);
340
341 pCodec = avcodec_find_decoder(hints.codec);
342
343 if(pCodec == NULL)
344 {
345 CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to find codec %d", hints.codec);
346 return false;
347 }
348
349 CLog::Log(LOGINFO, "CDVDVideoCodecFFmpeg::Open() Using codec: %s",
350 pCodec->long_name ? pCodec->long_name : pCodec->name);
351
352 m_pCodecContext = avcodec_alloc_context3(pCodec);
353 if (!m_pCodecContext)
354 return false;
355
356 m_pCodecContext->opaque = static_cast<ICallbackHWAccel*>(this);
357 m_pCodecContext->debug_mv = 0;
358 m_pCodecContext->debug = 0;
359 m_pCodecContext->workaround_bugs = FF_BUG_AUTODETECT;
360 m_pCodecContext->get_format = GetFormat;
361 m_pCodecContext->codec_tag = hints.codec_tag;
362
363 // setup threading model
364 if (!(hints.codecOptions & CODEC_FORCE_SOFTWARE))
365 {
366 if (m_decoderState == STATE_NONE)
367 {
368 m_decoderState = STATE_HW_SINGLE;
369 }
370 else
371 {
372 int num_threads = CServiceBroker::GetCPUInfo()->GetCPUCount() * 3 / 2;
373 num_threads = std::max(1, std::min(num_threads, 16));
374 m_pCodecContext->thread_count = num_threads;
375 m_pCodecContext->thread_safe_callbacks = 1;
376 m_decoderState = STATE_SW_MULTI;
377 CLog::Log(LOGDEBUG, "CDVDVideoCodecFFmpeg - open frame threaded with %d threads", num_threads);
378 }
379 }
380 else
381 m_decoderState = STATE_SW_SINGLE;
382
383 // if we don't do this, then some codecs seem to fail.
384 m_pCodecContext->coded_height = hints.height;
385 m_pCodecContext->coded_width = hints.width;
386 m_pCodecContext->bits_per_coded_sample = hints.bitsperpixel;
387
388 if( hints.extradata && hints.extrasize > 0 )
389 {
390 m_pCodecContext->extradata_size = hints.extrasize;
391 m_pCodecContext->extradata = (uint8_t*)av_mallocz(hints.extrasize + AV_INPUT_BUFFER_PADDING_SIZE);
392 memcpy(m_pCodecContext->extradata, hints.extradata, hints.extrasize);
393 }
394
395 // advanced setting override for skip loop filter (see avcodec.h for valid options)
396 //! @todo allow per video setting?
397 int iSkipLoopFilter = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iSkipLoopFilter;
398 if (iSkipLoopFilter != 0)
399 {
400 m_pCodecContext->skip_loop_filter = static_cast<AVDiscard>(iSkipLoopFilter);
401 }
402
403 // set any special options
404 for(std::vector<CDVDCodecOption>::iterator it = options.m_keys.begin(); it != options.m_keys.end(); ++it)
405 {
406 av_opt_set(m_pCodecContext, it->m_name.c_str(), it->m_value.c_str(), 0);
407 }
408
409 if (avcodec_open2(m_pCodecContext, pCodec, nullptr) < 0)
410 {
411 CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to open codec");
412 avcodec_free_context(&m_pCodecContext);
413 return false;
414 }
415
416 m_pFrame = av_frame_alloc();
417 if (!m_pFrame)
418 {
419 avcodec_free_context(&m_pCodecContext);
420 return false;
421 }
422
423 m_pDecodedFrame = av_frame_alloc();
424 if (!m_pDecodedFrame)
425 {
426 av_frame_free(&m_pFrame);
427 avcodec_free_context(&m_pCodecContext);
428 return false;
429 }
430
431 m_pFilterFrame = av_frame_alloc();
432 if (!m_pFilterFrame)
433 {
434 av_frame_free(&m_pFrame);
435 av_frame_free(&m_pDecodedFrame);
436 avcodec_free_context(&m_pCodecContext);
437 return false;
438 }
439
440 UpdateName();
441 const char* pixFmtName = av_get_pix_fmt_name(m_pCodecContext->pix_fmt);
442 m_processInfo.SetVideoDimensions(m_pCodecContext->coded_width, m_pCodecContext->coded_height);
443 m_processInfo.SetVideoPixelFormat(pixFmtName ? pixFmtName : "");
444
445 m_dropCtrl.Reset(true);
446 m_eof = false;
447 return true;
448 }
449
Dispose()450 void CDVDVideoCodecFFmpeg::Dispose()
451 {
452 av_frame_free(&m_pFrame);
453 av_frame_free(&m_pDecodedFrame);
454 av_frame_free(&m_pFilterFrame);
455 avcodec_free_context(&m_pCodecContext);
456 SAFE_RELEASE(m_pHardware);
457
458 FilterClose();
459 }
460
SetFilters()461 void CDVDVideoCodecFFmpeg::SetFilters()
462 {
463 // ask codec to do deinterlacing if possible
464 EINTERLACEMETHOD mInt = m_processInfo.GetVideoSettings().m_InterlaceMethod;
465
466 if (!m_processInfo.Supports(mInt))
467 mInt = m_processInfo.GetFallbackDeintMethod();
468
469 unsigned int filters = 0;
470
471 if (mInt != VS_INTERLACEMETHOD_NONE && m_interlaced)
472 {
473 if (mInt == VS_INTERLACEMETHOD_DEINTERLACE)
474 filters = FILTER_DEINTERLACE_ANY;
475 else if (mInt == VS_INTERLACEMETHOD_DEINTERLACE_HALF)
476 filters = FILTER_DEINTERLACE_ANY | FILTER_DEINTERLACE_HALFED;
477
478 if (filters)
479 filters |= FILTER_DEINTERLACE_FLAGGED;
480 }
481
482 if (m_codecControlFlags & DVD_CODEC_CTRL_ROTATE)
483 filters |= FILTER_ROTATE;
484
485 m_filters_next.clear();
486
487 if (filters & FILTER_ROTATE)
488 {
489 switch(m_iOrientation)
490 {
491 case 90:
492 m_filters_next += "transpose=1";
493 break;
494 case 180:
495 m_filters_next += "vflip,hflip";
496 break;
497 case 270:
498 m_filters_next += "transpose=2";
499 break;
500 default:
501 break;
502 }
503 }
504
505 if (filters & FILTER_DEINTERLACE_YADIF)
506 {
507 if (filters & FILTER_DEINTERLACE_HALFED)
508 m_filters_next = "yadif=0:-1";
509 else
510 m_filters_next = "yadif=1:-1";
511
512 if (filters & FILTER_DEINTERLACE_FLAGGED)
513 m_filters_next += ":1";
514 }
515 }
516
UpdateName()517 void CDVDVideoCodecFFmpeg::UpdateName()
518 {
519 if(m_pCodecContext->codec->name)
520 m_name = std::string("ff-") + m_pCodecContext->codec->name;
521 else
522 m_name = "ffmpeg";
523
524 if(m_pHardware)
525 m_name += "-" + m_pHardware->Name();
526
527 m_processInfo.SetVideoDecoderName(m_name, m_pHardware ? true : false);
528
529 CLog::Log(LOGDEBUG, "CDVDVideoCodecFFmpeg - Updated codec: %s", m_name.c_str());
530 }
531
532 union pts_union
533 {
534 double pts_d;
535 int64_t pts_i;
536 };
537
pts_dtoi(double pts)538 static int64_t pts_dtoi(double pts)
539 {
540 pts_union u;
541 u.pts_d = pts;
542 return u.pts_i;
543 }
544
AddData(const DemuxPacket & packet)545 bool CDVDVideoCodecFFmpeg::AddData(const DemuxPacket &packet)
546 {
547 if (!m_pCodecContext)
548 return true;
549
550 if (!packet.pData)
551 return true;
552
553 if (m_eof)
554 {
555 Reset();
556 }
557
558 if (packet.recoveryPoint)
559 m_started = true;
560
561 m_dts = packet.dts;
562 m_pCodecContext->reordered_opaque = pts_dtoi(packet.pts);
563
564 AVPacket avpkt;
565 av_init_packet(&avpkt);
566 avpkt.data = packet.pData;
567 avpkt.size = packet.iSize;
568 avpkt.dts = (packet.dts == DVD_NOPTS_VALUE) ? AV_NOPTS_VALUE : static_cast<int64_t>(packet.dts / DVD_TIME_BASE * AV_TIME_BASE);
569 avpkt.pts = (packet.pts == DVD_NOPTS_VALUE) ? AV_NOPTS_VALUE : static_cast<int64_t>(packet.pts / DVD_TIME_BASE * AV_TIME_BASE);
570 avpkt.side_data = static_cast<AVPacketSideData*>(packet.pSideData);
571 avpkt.side_data_elems = packet.iSideDataElems;
572
573 int ret = avcodec_send_packet(m_pCodecContext, &avpkt);
574
575 // try again
576 if (ret == AVERROR(EAGAIN))
577 {
578 return false;
579 }
580 // error
581 else if (ret)
582 {
583 // handle VC_NOBUFFER error for hw accel
584 if (m_pHardware)
585 {
586 int result = m_pHardware->Check(m_pCodecContext);
587 if (result == VC_NOBUFFER)
588 {
589 return false;
590 }
591 }
592 }
593
594 m_iLastKeyframe++;
595 // put a limit on convergence count to avoid huge mem usage on streams without keyframes
596 if (m_iLastKeyframe > 300)
597 m_iLastKeyframe = 300;
598
599 m_startedInput = true;
600
601 return true;
602 }
603
GetPicture(VideoPicture * pVideoPicture)604 CDVDVideoCodec::VCReturn CDVDVideoCodecFFmpeg::GetPicture(VideoPicture* pVideoPicture)
605 {
606 if (!m_startedInput)
607 {
608 return VC_BUFFER;
609 }
610 else if (m_eof)
611 {
612 return VC_EOF;
613 }
614
615 // handle hw accelerators first, they may have frames ready
616 if (m_pHardware)
617 {
618 int flags = m_codecControlFlags;
619 flags &= ~DVD_CODEC_CTRL_DRAIN;
620 m_pHardware->SetCodecControl(flags);
621 CDVDVideoCodec::VCReturn ret = m_pHardware->Decode(m_pCodecContext, nullptr);
622 if (ret == VC_PICTURE)
623 {
624 if (m_pHardware->GetPicture(m_pCodecContext, pVideoPicture))
625 return VC_PICTURE;
626 else
627 return VC_ERROR;
628 }
629 else if (ret == VC_BUFFER)
630 ;
631 else
632 return ret;
633 }
634 else if (m_pFilterGraph && !m_filterEof)
635 {
636 CDVDVideoCodec::VCReturn ret = FilterProcess(nullptr);
637 if (ret == VC_PICTURE)
638 {
639 if (!SetPictureParams(pVideoPicture))
640 return VC_ERROR;
641 return VC_PICTURE;
642 }
643 else if (ret == VC_BUFFER)
644 ;
645 else
646 return ret;
647 }
648
649 // process ffmpeg
650 if (m_codecControlFlags & DVD_CODEC_CTRL_DRAIN)
651 {
652 AVPacket avpkt;
653 av_init_packet(&avpkt);
654 avpkt.data = nullptr;
655 avpkt.size = 0;
656 avpkt.dts = AV_NOPTS_VALUE;
657 avpkt.pts = AV_NOPTS_VALUE;
658 avcodec_send_packet(m_pCodecContext, &avpkt);
659 }
660
661 int ret = avcodec_receive_frame(m_pCodecContext, m_pDecodedFrame);
662
663 if (m_decoderState == STATE_HW_FAILED && !m_pHardware)
664 return VC_REOPEN;
665
666 if(m_iLastKeyframe < m_pCodecContext->has_b_frames + 2)
667 m_iLastKeyframe = m_pCodecContext->has_b_frames + 2;
668
669 if (ret == AVERROR_EOF)
670 {
671 // next drain hw accel or filter
672 if (m_pHardware)
673 {
674 int flags = m_codecControlFlags;
675 flags |= DVD_CODEC_CTRL_DRAIN;
676 m_pHardware->SetCodecControl(flags);
677 int ret = m_pHardware->Decode(m_pCodecContext, nullptr);
678 if (ret == VC_PICTURE)
679 {
680 if (m_pHardware->GetPicture(m_pCodecContext, pVideoPicture))
681 return VC_PICTURE;
682 else
683 return VC_ERROR;
684 }
685 else
686 {
687 m_eof = true;
688 CLog::Log(LOGDEBUG, "CDVDVideoCodecFFmpeg::GetPicture - eof hw accel");
689 return VC_EOF;
690 }
691 }
692 else if (m_pFilterGraph && !m_filterEof)
693 {
694 int ret = FilterProcess(nullptr);
695 if (ret == VC_PICTURE)
696 {
697 if (!SetPictureParams(pVideoPicture))
698 return VC_ERROR;
699 else
700 return VC_PICTURE;
701 }
702 else
703 {
704 m_eof = true;
705 CLog::Log(LOGDEBUG, "CDVDVideoCodecFFmpeg::GetPicture - eof filter");
706 return VC_EOF;
707 }
708 }
709 else
710 {
711 m_eof = true;
712 CLog::Log(LOGDEBUG, "CDVDVideoCodecFFmpeg::GetPicture - eof");
713 return VC_EOF;
714 }
715 }
716 else if (ret == AVERROR(EAGAIN))
717 {
718 return VC_BUFFER;
719 }
720 else if (ret)
721 {
722 CLog::Log(LOGERROR, "%s - avcodec_receive_frame returned failure", __FUNCTION__);
723 return VC_ERROR;
724 }
725
726 // here we got a frame
727 int64_t framePTS = m_pDecodedFrame->best_effort_timestamp;
728
729 if (m_pCodecContext->skip_frame > AVDISCARD_DEFAULT)
730 {
731 if (m_dropCtrl.m_state == CDropControl::VALID &&
732 m_dropCtrl.m_lastPTS != AV_NOPTS_VALUE &&
733 framePTS != AV_NOPTS_VALUE &&
734 framePTS > (m_dropCtrl.m_lastPTS + m_dropCtrl.m_diffPTS * 1.5))
735 {
736 m_droppedFrames++;
737 if (m_interlaced)
738 m_droppedFrames++;
739 }
740 }
741 m_dropCtrl.Process(framePTS, m_pCodecContext->skip_frame > AVDISCARD_DEFAULT);
742
743 if (m_pDecodedFrame->key_frame)
744 {
745 m_started = true;
746 m_iLastKeyframe = m_pCodecContext->has_b_frames + 2;
747 }
748 if (m_pDecodedFrame->interlaced_frame)
749 m_interlaced = true;
750 else
751 m_interlaced = false;
752
753 if (!m_processInfo.GetVideoInterlaced() && m_interlaced)
754 m_processInfo.SetVideoInterlaced(m_interlaced);
755
756 if (!m_started)
757 {
758 int frames = 300;
759 if (m_dropCtrl.m_state == CDropControl::VALID)
760 frames = static_cast<int>(6000000 / m_dropCtrl.m_diffPTS);
761 if (m_iLastKeyframe >= frames && m_pDecodedFrame->pict_type == AV_PICTURE_TYPE_I)
762 {
763 m_started = true;
764 }
765 else
766 {
767 av_frame_unref(m_pDecodedFrame);
768 return VC_BUFFER;
769 }
770 }
771
772 // push the frame to hw decoder for further processing
773 if (m_pHardware)
774 {
775 av_frame_unref(m_pFrame);
776 av_frame_move_ref(m_pFrame, m_pDecodedFrame);
777 CDVDVideoCodec::VCReturn ret = m_pHardware->Decode(m_pCodecContext, m_pFrame);
778 if (ret == VC_FLUSHED)
779 {
780 Reset();
781 return ret;
782 }
783 else if (ret == VC_FATAL)
784 {
785 m_decoderState = STATE_HW_FAILED;
786 return VC_REOPEN;
787 }
788 else if (ret == VC_PICTURE)
789 {
790 if (m_pHardware->GetPicture(m_pCodecContext, pVideoPicture))
791 return VC_PICTURE;
792 else
793 return VC_ERROR;
794 }
795
796 return ret;
797 }
798 // process filters for sw decoding
799 else
800 {
801 SetFilters();
802
803 bool need_scale = std::find(m_formats.begin(),
804 m_formats.end(),
805 m_pCodecContext->pix_fmt) == m_formats.end();
806
807 bool need_reopen = false;
808 if (m_filters != m_filters_next)
809 need_reopen = true;
810
811 if (!m_filters_next.empty() && m_filterEof)
812 need_reopen = true;
813
814 if (m_pFilterIn)
815 {
816 if (m_pFilterIn->outputs[0]->format != m_pCodecContext->pix_fmt ||
817 m_pFilterIn->outputs[0]->w != m_pCodecContext->width ||
818 m_pFilterIn->outputs[0]->h != m_pCodecContext->height)
819 need_reopen = true;
820 }
821
822 // try to setup new filters
823 if (need_reopen || (need_scale && m_pFilterGraph == nullptr))
824 {
825 m_filters = m_filters_next;
826
827 if (FilterOpen(m_filters, need_scale) < 0)
828 FilterClose();
829 }
830
831 if (m_pFilterGraph && !m_filterEof)
832 {
833 CDVDVideoCodec::VCReturn ret = FilterProcess(m_pDecodedFrame);
834 if (ret != VC_PICTURE)
835 return VC_NONE;
836 }
837 else
838 {
839 av_frame_unref(m_pFrame);
840 av_frame_move_ref(m_pFrame, m_pDecodedFrame);
841 }
842
843 if (!SetPictureParams(pVideoPicture))
844 return VC_ERROR;
845 else
846 return VC_PICTURE;
847 }
848
849 return VC_NONE;
850 }
851
SetPictureParams(VideoPicture * pVideoPicture)852 bool CDVDVideoCodecFFmpeg::SetPictureParams(VideoPicture* pVideoPicture)
853 {
854 if (!GetPictureCommon(pVideoPicture))
855 return false;
856
857 pVideoPicture->iFlags |= m_pFrame->data[0] ? 0 : DVP_FLAG_DROPPED;
858
859 if (pVideoPicture->videoBuffer)
860 pVideoPicture->videoBuffer->Release();
861 pVideoPicture->videoBuffer = nullptr;
862
863 CVideoBufferFFmpeg *buffer = dynamic_cast<CVideoBufferFFmpeg*>(m_videoBufferPool->Get());
864 buffer->SetRef(m_pFrame);
865 pVideoPicture->videoBuffer = buffer;
866
867 if (m_processInfo.GetVideoSettings().m_PostProcess)
868 {
869 m_postProc.SetType(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoPPFFmpegPostProc, false);
870 m_postProc.Process(pVideoPicture);
871 }
872
873 return true;
874 }
875
Reset()876 void CDVDVideoCodecFFmpeg::Reset()
877 {
878 m_started = false;
879 m_startedInput = false;
880 m_interlaced = false;
881 m_decoderPts = DVD_NOPTS_VALUE;
882 m_skippedDeint = 0;
883 m_droppedFrames = 0;
884 m_eof = false;
885 m_iLastKeyframe = m_pCodecContext->has_b_frames;
886 avcodec_flush_buffers(m_pCodecContext);
887 av_frame_unref(m_pFrame);
888
889 if (m_pHardware)
890 m_pHardware->Reset();
891
892 m_filters = "";
893 FilterClose();
894 m_dropCtrl.Reset(false);
895 }
896
Reopen()897 void CDVDVideoCodecFFmpeg::Reopen()
898 {
899 Dispose();
900 if (!Open(m_hints, m_options))
901 {
902 Dispose();
903 }
904 }
905
GetPictureCommon(VideoPicture * pVideoPicture)906 bool CDVDVideoCodecFFmpeg::GetPictureCommon(VideoPicture* pVideoPicture)
907 {
908 if (!m_pFrame)
909 return false;
910
911 pVideoPicture->iWidth = m_pFrame->width;
912 pVideoPicture->iHeight = m_pFrame->height;
913
914 /* crop of 10 pixels if demuxer asked it */
915 if(m_pCodecContext->coded_width && m_pCodecContext->coded_width < (int)pVideoPicture->iWidth
916 && m_pCodecContext->coded_width > (int)pVideoPicture->iWidth - 10)
917 pVideoPicture->iWidth = m_pCodecContext->coded_width;
918
919 if(m_pCodecContext->coded_height && m_pCodecContext->coded_height < (int)pVideoPicture->iHeight
920 && m_pCodecContext->coded_height > (int)pVideoPicture->iHeight - 10)
921 pVideoPicture->iHeight = m_pCodecContext->coded_height;
922
923 double aspect_ratio;
924
925 /* use variable in the frame */
926 AVRational pixel_aspect = m_pFrame->sample_aspect_ratio;
927
928 if (pixel_aspect.num == 0)
929 aspect_ratio = 0;
930 else
931 aspect_ratio = av_q2d(pixel_aspect) * pVideoPicture->iWidth / pVideoPicture->iHeight;
932
933 if (aspect_ratio <= 0.0)
934 aspect_ratio = (float)pVideoPicture->iWidth / (float)pVideoPicture->iHeight;
935
936 if (m_DAR != aspect_ratio)
937 {
938 m_DAR = aspect_ratio;
939 m_processInfo.SetVideoDAR(static_cast<float>(m_DAR));
940 }
941
942 /* XXX: we suppose the screen has a 1.0 pixel ratio */ // CDVDVideo will compensate it.
943 pVideoPicture->iDisplayHeight = pVideoPicture->iHeight;
944 pVideoPicture->iDisplayWidth = ((int)RINT(pVideoPicture->iHeight * aspect_ratio)) & -3;
945 if (pVideoPicture->iDisplayWidth > pVideoPicture->iWidth)
946 {
947 pVideoPicture->iDisplayWidth = pVideoPicture->iWidth;
948 pVideoPicture->iDisplayHeight = ((int)RINT(pVideoPicture->iWidth / aspect_ratio)) & -3;
949 }
950
951
952 pVideoPicture->pts = DVD_NOPTS_VALUE;
953
954 AVDictionaryEntry * entry = av_dict_get(m_pFrame->metadata, "stereo_mode", NULL, 0);
955 if(entry && entry->value)
956 {
957 pVideoPicture->stereoMode = (const char*)entry->value;
958 }
959 else
960 pVideoPicture->stereoMode.clear();
961
962 pVideoPicture->iRepeatPicture = 0.5 * m_pFrame->repeat_pict;
963 pVideoPicture->iFlags = 0;
964 pVideoPicture->iFlags |= m_pFrame->interlaced_frame ? DVP_FLAG_INTERLACED : 0;
965 pVideoPicture->iFlags |= m_pFrame->top_field_first ? DVP_FLAG_TOP_FIELD_FIRST: 0;
966
967 if (m_codecControlFlags & DVD_CODEC_CTRL_DROP)
968 {
969 pVideoPicture->iFlags |= DVP_FLAG_DROPPED;
970 }
971
972 pVideoPicture->pixelFormat = m_pCodecContext->sw_pix_fmt;
973
974 pVideoPicture->chroma_position = m_pCodecContext->chroma_sample_location;
975 pVideoPicture->color_primaries = m_pCodecContext->color_primaries == AVCOL_PRI_UNSPECIFIED ? m_hints.colorPrimaries : m_pCodecContext->color_primaries;
976 pVideoPicture->color_transfer = m_pCodecContext->color_trc == AVCOL_TRC_UNSPECIFIED ? m_hints.colorTransferCharacteristic : m_pCodecContext->color_trc;
977 pVideoPicture->color_space = m_pCodecContext->colorspace == AVCOL_SPC_UNSPECIFIED ? m_hints.colorSpace : m_pCodecContext->colorspace;
978 pVideoPicture->colorBits = 8;
979
980 // determine how number of bits of encoded video
981 if (m_pCodecContext->pix_fmt == AV_PIX_FMT_YUV420P12)
982 pVideoPicture->colorBits = 12;
983 else if (m_pCodecContext->pix_fmt == AV_PIX_FMT_YUV420P10)
984 pVideoPicture->colorBits = 10;
985 else if (m_pCodecContext->codec_id == AV_CODEC_ID_HEVC &&
986 m_pCodecContext->profile == FF_PROFILE_HEVC_MAIN_10)
987 pVideoPicture->colorBits = 10;
988 else if (m_pCodecContext->codec_id == AV_CODEC_ID_H264 &&
989 (m_pCodecContext->profile == FF_PROFILE_H264_HIGH_10||
990 m_pCodecContext->profile == FF_PROFILE_H264_HIGH_10_INTRA))
991 pVideoPicture->colorBits = 10;
992 else if (m_pCodecContext->codec_id == AV_CODEC_ID_VP9 &&
993 (m_pCodecContext->profile == FF_PROFILE_VP9_2 ||
994 m_pCodecContext->profile == FF_PROFILE_VP9_3))
995 pVideoPicture->colorBits = 10;
996
997 if (m_pCodecContext->color_range == AVCOL_RANGE_JPEG ||
998 m_pCodecContext->pix_fmt == AV_PIX_FMT_YUVJ420P)
999 pVideoPicture->color_range = 1;
1000 else
1001 pVideoPicture->color_range = m_hints.colorRange == AVCOL_RANGE_JPEG ? 1 : 0;
1002
1003 pVideoPicture->qp_table = av_frame_get_qp_table(m_pFrame,
1004 &pVideoPicture->qstride,
1005 &pVideoPicture->qscale_type);
1006 pVideoPicture->pict_type = m_pFrame->pict_type;
1007
1008 // metadata
1009 pVideoPicture->hasDisplayMetadata = false;
1010 pVideoPicture->hasLightMetadata = false;
1011 AVFrameSideData *sd = av_frame_get_side_data(m_pFrame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
1012 if (sd)
1013 {
1014 pVideoPicture->displayMetadata = *(AVMasteringDisplayMetadata *)sd->data;
1015 pVideoPicture->hasDisplayMetadata = true;
1016 }
1017 else if (m_hints.masteringMetadata)
1018 {
1019 pVideoPicture->displayMetadata = *m_hints.masteringMetadata.get();
1020 pVideoPicture->hasDisplayMetadata = true;
1021 }
1022 sd = av_frame_get_side_data(m_pFrame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
1023 if (sd)
1024 {
1025 pVideoPicture->lightMetadata = *(AVContentLightMetadata *)sd->data;
1026 pVideoPicture->hasLightMetadata = true;
1027 }
1028 else if (m_hints.contentLightMetadata)
1029 {
1030 pVideoPicture->lightMetadata = *m_hints.contentLightMetadata.get();
1031 pVideoPicture->hasLightMetadata = true;
1032 }
1033
1034 if (pVideoPicture->iRepeatPicture)
1035 pVideoPicture->dts = DVD_NOPTS_VALUE;
1036 else
1037 pVideoPicture->dts = m_dts;
1038
1039 m_dts = DVD_NOPTS_VALUE;
1040
1041 int64_t bpts = m_pFrame->best_effort_timestamp;
1042 if (bpts != AV_NOPTS_VALUE)
1043 {
1044 pVideoPicture->pts = (double)bpts * DVD_TIME_BASE / AV_TIME_BASE;
1045 if (pVideoPicture->pts == m_decoderPts)
1046 {
1047 pVideoPicture->iRepeatPicture = -0.5;
1048 pVideoPicture->pts = DVD_NOPTS_VALUE;
1049 pVideoPicture->dts = DVD_NOPTS_VALUE;
1050 }
1051 }
1052 else
1053 pVideoPicture->pts = DVD_NOPTS_VALUE;
1054
1055 if (pVideoPicture->pts != DVD_NOPTS_VALUE)
1056 m_decoderPts = pVideoPicture->pts;
1057
1058 if (m_requestSkipDeint)
1059 {
1060 pVideoPicture->iFlags |= DVD_CODEC_CTRL_SKIPDEINT;
1061 m_skippedDeint++;
1062 }
1063
1064 m_requestSkipDeint = false;
1065 pVideoPicture->iFlags |= m_codecControlFlags;
1066
1067 return true;
1068 }
1069
FilterOpen(const std::string & filters,bool scale)1070 int CDVDVideoCodecFFmpeg::FilterOpen(const std::string& filters, bool scale)
1071 {
1072 int result;
1073
1074 if (m_pFilterGraph)
1075 FilterClose();
1076
1077 if (filters.empty() && !scale)
1078 return 0;
1079
1080 if (m_pHardware)
1081 {
1082 CLog::Log(LOGWARNING, "CDVDVideoCodecFFmpeg::FilterOpen - skipped opening filters on hardware decode");
1083 return 0;
1084 }
1085
1086 if (!(m_pFilterGraph = avfilter_graph_alloc()))
1087 {
1088 CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - unable to alloc filter graph");
1089 return -1;
1090 }
1091
1092 const AVFilter* srcFilter = avfilter_get_by_name("buffer");
1093 const AVFilter* outFilter = avfilter_get_by_name("buffersink"); // should be last filter in the graph for now
1094
1095 std::string args = StringUtils::Format("%d:%d:%d:%d:%d:%d:%d",
1096 m_pCodecContext->width,
1097 m_pCodecContext->height,
1098 m_pCodecContext->pix_fmt,
1099 m_pCodecContext->time_base.num ? m_pCodecContext->time_base.num : 1,
1100 m_pCodecContext->time_base.num ? m_pCodecContext->time_base.den : 1,
1101 m_pCodecContext->sample_aspect_ratio.num != 0 ? m_pCodecContext->sample_aspect_ratio.num : 1,
1102 m_pCodecContext->sample_aspect_ratio.num != 0 ? m_pCodecContext->sample_aspect_ratio.den : 1);
1103
1104 if ((result = avfilter_graph_create_filter(&m_pFilterIn, srcFilter, "src", args.c_str(), NULL, m_pFilterGraph)) < 0)
1105 {
1106 CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - avfilter_graph_create_filter: src");
1107 return result;
1108 }
1109
1110 if ((result = avfilter_graph_create_filter(&m_pFilterOut, outFilter, "out", NULL, NULL, m_pFilterGraph)) < 0)
1111 {
1112 CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - avfilter_graph_create_filter: out");
1113 return result;
1114 }
1115 if ((result = av_opt_set_int_list(m_pFilterOut, "pix_fmts", &m_formats[0], AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0)
1116 {
1117 CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - failed settings pix formats");
1118 return result;
1119 }
1120
1121 if (!filters.empty())
1122 {
1123 AVFilterInOut* outputs = avfilter_inout_alloc();
1124 AVFilterInOut* inputs = avfilter_inout_alloc();
1125
1126 outputs->name = av_strdup("in");
1127 outputs->filter_ctx = m_pFilterIn;
1128 outputs->pad_idx = 0;
1129 outputs->next = nullptr;
1130
1131 inputs->name = av_strdup("out");
1132 inputs->filter_ctx = m_pFilterOut;
1133 inputs->pad_idx = 0;
1134 inputs->next = nullptr;
1135
1136 result = avfilter_graph_parse_ptr(m_pFilterGraph, m_filters.c_str(), &inputs, &outputs, NULL);
1137 avfilter_inout_free(&outputs);
1138 avfilter_inout_free(&inputs);
1139
1140 if (result < 0)
1141 {
1142 CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - avfilter_graph_parse");
1143 return result;
1144 }
1145
1146 if (filters.compare(0,5,"yadif") == 0)
1147 {
1148 m_processInfo.SetVideoDeintMethod(filters);
1149 }
1150 else
1151 {
1152 m_processInfo.SetVideoDeintMethod("none");
1153 }
1154 }
1155 else
1156 {
1157 if ((result = avfilter_link(m_pFilterIn, 0, m_pFilterOut, 0)) < 0)
1158 {
1159 CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - avfilter_link");
1160 return result;
1161 }
1162
1163 m_processInfo.SetVideoDeintMethod("none");
1164 }
1165
1166 if ((result = avfilter_graph_config(m_pFilterGraph, nullptr)) < 0)
1167 {
1168 CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - avfilter_graph_config");
1169 return result;
1170 }
1171
1172 if (CServiceBroker::GetLogging().CanLogComponent(LOGVIDEO))
1173 {
1174 char* graphDump = avfilter_graph_dump(m_pFilterGraph, nullptr);
1175 if (graphDump)
1176 {
1177 CLog::Log(LOGDEBUG, "CDVDVideoCodecFFmpeg::FilterOpen - Final filter graph:\n%s", graphDump);
1178 av_freep(&graphDump);
1179 }
1180 }
1181
1182 m_filterEof = false;
1183 return result;
1184 }
1185
FilterClose()1186 void CDVDVideoCodecFFmpeg::FilterClose()
1187 {
1188 if (m_pFilterGraph)
1189 {
1190 CLog::Log(LOGDEBUG, LOGVIDEO, "CDVDVideoCodecFFmpeg::FilterClose - Freeing filter graph");
1191 avfilter_graph_free(&m_pFilterGraph);
1192
1193 // Disposed by above code
1194 m_pFilterIn = nullptr;
1195 m_pFilterOut = nullptr;
1196 }
1197 }
1198
FilterProcess(AVFrame * frame)1199 CDVDVideoCodec::VCReturn CDVDVideoCodecFFmpeg::FilterProcess(AVFrame* frame)
1200 {
1201 int result;
1202
1203 if (frame || (m_codecControlFlags & DVD_CODEC_CTRL_DRAIN))
1204 {
1205 result = av_buffersrc_add_frame(m_pFilterIn, frame);
1206 if (result < 0)
1207 {
1208 CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterProcess - av_buffersrc_add_frame");
1209 return VC_ERROR;
1210 }
1211 }
1212
1213 result = av_buffersink_get_frame(m_pFilterOut, m_pFilterFrame);
1214
1215 if (result == AVERROR(EAGAIN))
1216 return VC_BUFFER;
1217 else if (result == AVERROR_EOF)
1218 {
1219 result = av_buffersink_get_frame(m_pFilterOut, m_pFilterFrame);
1220 m_filterEof = true;
1221 if (result < 0)
1222 return VC_BUFFER;
1223 }
1224 else if (result < 0)
1225 {
1226 CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterProcess - av_buffersink_get_frame");
1227 return VC_ERROR;
1228 }
1229
1230 av_frame_unref(m_pFrame);
1231 av_frame_move_ref(m_pFrame, m_pFilterFrame);
1232
1233 return VC_PICTURE;
1234 }
1235
GetConvergeCount()1236 unsigned CDVDVideoCodecFFmpeg::GetConvergeCount()
1237 {
1238 return m_iLastKeyframe;
1239 }
1240
GetAllowedReferences()1241 unsigned CDVDVideoCodecFFmpeg::GetAllowedReferences()
1242 {
1243 if(m_pHardware)
1244 return m_pHardware->GetAllowedReferences();
1245 else
1246 return 0;
1247 }
1248
GetCodecStats(double & pts,int & droppedFrames,int & skippedPics)1249 bool CDVDVideoCodecFFmpeg::GetCodecStats(double &pts, int &droppedFrames, int &skippedPics)
1250 {
1251 if (m_decoderPts != DVD_NOPTS_VALUE)
1252 pts = m_decoderPts;
1253 else
1254 pts = m_dts;
1255
1256 if (m_droppedFrames)
1257 droppedFrames = m_droppedFrames;
1258 else
1259 droppedFrames = -1;
1260 m_droppedFrames = 0;
1261
1262 if (m_skippedDeint)
1263 skippedPics = m_skippedDeint;
1264 else
1265 skippedPics = -1;
1266 m_skippedDeint = 0;
1267
1268 return true;
1269 }
1270
SetCodecControl(int flags)1271 void CDVDVideoCodecFFmpeg::SetCodecControl(int flags)
1272 {
1273 m_codecControlFlags = flags;
1274
1275 if (m_pCodecContext)
1276 {
1277 bool bDrop = (flags & DVD_CODEC_CTRL_DROP_ANY) != 0;
1278 if (bDrop && m_pHardware && m_pHardware->CanSkipDeint())
1279 {
1280 m_requestSkipDeint = true;
1281 bDrop = false;
1282 }
1283 else
1284 m_requestSkipDeint = false;
1285
1286 if (bDrop)
1287 {
1288 m_pCodecContext->skip_frame = AVDISCARD_NONREF;
1289 m_pCodecContext->skip_idct = AVDISCARD_NONREF;
1290 m_pCodecContext->skip_loop_filter = AVDISCARD_NONREF;
1291 }
1292 else
1293 {
1294 m_pCodecContext->skip_frame = AVDISCARD_DEFAULT;
1295 m_pCodecContext->skip_idct = AVDISCARD_DEFAULT;
1296 m_pCodecContext->skip_loop_filter = AVDISCARD_DEFAULT;
1297 }
1298 }
1299
1300 if (m_pHardware)
1301 m_pHardware->SetCodecControl(flags);
1302 }
1303
SetHardware(IHardwareDecoder * hardware)1304 void CDVDVideoCodecFFmpeg::SetHardware(IHardwareDecoder* hardware)
1305 {
1306 SAFE_RELEASE(m_pHardware);
1307 m_pHardware = hardware;
1308 UpdateName();
1309 }
1310
GetHWAccel()1311 IHardwareDecoder* CDVDVideoCodecFFmpeg::GetHWAccel()
1312 {
1313 return m_pHardware;
1314 }
1315
1316