Merge pull request #773 from CrHasher/yadif_hwdfix
[vuplus_xbmc] / xbmc / cores / dvdplayer / DVDCodecs / Video / DVDVideoCodecFFmpeg.cpp
index 0c0eca3..fdd1112 100644 (file)
@@ -123,12 +123,11 @@ enum PixelFormat CDVDVideoCodecFFmpeg::GetFormat( struct AVCodecContext * avctx
 CDVDVideoCodecFFmpeg::CDVDVideoCodecFFmpeg() : CDVDVideoCodec()
 {
   m_pCodecContext = NULL;
-  m_pConvertFrame = NULL;
   m_pFrame = NULL;
   m_pFilterGraph  = NULL;
   m_pFilterIn     = NULL;
   m_pFilterOut    = NULL;
-  m_pFilterLink   = NULL;
+  m_pBufferRef    = NULL;
 
   m_iPictureWidth = 0;
   m_iPictureHeight = 0;
@@ -163,9 +162,14 @@ bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options
   m_dllAvFilter.avfilter_register_all();
 
   m_bSoftware     = hints.software;
-  m_pCodecContext = m_dllAvCodec.avcodec_alloc_context();
+  m_iOrientation  = hints.orientation;
+
+  m_formats.push_back(PIX_FMT_YUV420P);
+  m_formats.push_back(PIX_FMT_YUVJ420P);
+  m_formats.push_back(PIX_FMT_NONE); /* always add none to get a terminated list in ffmpeg world */
 
   pCodec = NULL;
+  m_pCodecContext = NULL;
 
   if (hints.codec == CODEC_ID_H264)
   {
@@ -196,6 +200,7 @@ bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options
 
         CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::Open() Creating VDPAU(%ix%i, %d)",hints.width, hints.height, hints.codec);
         CVDPAU* vdp = new CVDPAU();
+        m_pCodecContext = m_dllAvCodec.avcodec_alloc_context3(pCodec);
         m_pCodecContext->codec_id = hints.codec;
         m_pCodecContext->width    = hints.width;
         m_pCodecContext->height   = hints.height;
@@ -207,7 +212,7 @@ bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options
           m_pCodecContext->codec_id = CODEC_ID_NONE; // ffmpeg will complain if this has been set
           break;
         }
-        m_pCodecContext->codec_id = CODEC_ID_NONE; // ffmpeg will complain if this has been set
+        m_dllAvUtil.av_freep(&m_pCodecContext);
         CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::Open() Failed to get VDPAU device");
         vdp->Release();
       }
@@ -226,21 +231,26 @@ bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options
 
   CLog::Log(LOGNOTICE,"CDVDVideoCodecFFmpeg::Open() Using codec: %s",pCodec->long_name ? pCodec->long_name : pCodec->name);
 
+  if(m_pCodecContext == NULL)
+    m_pCodecContext = m_dllAvCodec.avcodec_alloc_context3(pCodec);
+
   m_pCodecContext->opaque = (void*)this;
   m_pCodecContext->debug_mv = 0;
   m_pCodecContext->debug = 0;
   m_pCodecContext->workaround_bugs = FF_BUG_AUTODETECT;
   m_pCodecContext->get_format = GetFormat;
   m_pCodecContext->codec_tag = hints.codec_tag;
+  /* Only allow slice threading, since frame threading is more
+   * sensitive to changes in frame sizes, and it causes crashes
+   * during HW accell */
+  m_pCodecContext->thread_type = FF_THREAD_SLICE;
 
 #if defined(__APPLE__) && defined(__arm__)
   // ffmpeg with enabled neon will crash and burn if this is enabled
   m_pCodecContext->flags &= CODEC_FLAG_EMU_EDGE;
 #else
   if (pCodec->id != CODEC_ID_H264 && pCodec->capabilities & CODEC_CAP_DR1
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,69,0)
       && pCodec->id != CODEC_ID_VP8
-#endif
      )
     m_pCodecContext->flags |= CODEC_FLAG_EMU_EDGE;
 #endif
@@ -272,16 +282,16 @@ bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options
     if (it->m_name == "surfaces")
       m_uSurfacesCount = std::atoi(it->m_value.c_str());
     else
-      m_dllAvUtil.av_set_string3(m_pCodecContext, it->m_name.c_str(), it->m_value.c_str(), 0, NULL);
+      m_dllAvUtil.av_opt_set(m_pCodecContext, it->m_name.c_str(), it->m_value.c_str(), 0);
   }
 
   int num_threads = std::min(8 /*MAX_THREADS*/, g_cpuInfo.getCPUCount());
   if( num_threads > 1 && !hints.software && m_pHardware == NULL // thumbnail extraction fails when run threaded
   && ( pCodec->id == CODEC_ID_H264
     || pCodec->id == CODEC_ID_MPEG4 ))
-    m_dllAvCodec.avcodec_thread_init(m_pCodecContext, num_threads);
+    m_pCodecContext->thread_count = num_threads;
 
-  if (m_dllAvCodec.avcodec_open(m_pCodecContext, pCodec) < 0)
+  if (m_dllAvCodec.avcodec_open2(m_pCodecContext, pCodec, NULL) < 0)
   {
     CLog::Log(LOGDEBUG,"CDVDVideoCodecFFmpeg::Open() Unable to open codec");
     return false;
@@ -290,14 +300,7 @@ bool CDVDVideoCodecFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options
   m_pFrame = m_dllAvCodec.avcodec_alloc_frame();
   if (!m_pFrame) return false;
 
-  if(pCodec->name)
-    m_name = CStdString("ff-") + pCodec->name;
-  else
-    m_name = "ffmpeg";
-
-  if(m_pHardware)
-    m_name += "-" + m_pHardware->Name();
-
+  UpdateName();
   return true;
 }
 
@@ -306,13 +309,6 @@ void CDVDVideoCodecFFmpeg::Dispose()
   if (m_pFrame) m_dllAvUtil.av_free(m_pFrame);
   m_pFrame = NULL;
 
-  if (m_pConvertFrame)
-  {
-    m_dllAvCodec.avpicture_free(m_pConvertFrame);
-    m_dllAvUtil.av_free(m_pConvertFrame);
-  }
-  m_pConvertFrame = NULL;
-
   if (m_pCodecContext)
   {
     if (m_pCodecContext->codec) m_dllAvCodec.avcodec_close(m_pCodecContext);
@@ -458,9 +454,6 @@ int CDVDVideoCodecFFmpeg::Decode(BYTE* pData, int iSize, double dts, double pts)
     return VC_ERROR;
   }
 
-  if (len != iSize && m_pCodecContext->skip_frame != AVDISCARD_NONREF)
-    CLog::Log(LOGWARNING, "%s - avcodec_decode_video didn't consume the full packet. size: %d, consumed: %d", __FUNCTION__, iSize, len);
-
   if (!iGotPicture)
     return VC_BUFFER;
 
@@ -478,67 +471,32 @@ int CDVDVideoCodecFFmpeg::Decode(BYTE* pData, int iSize, double dts, double pts)
   if(m_pCodecContext->codec_id == CODEC_ID_H264)
     m_started = true;
 
-  if(m_pCodecContext->pix_fmt != PIX_FMT_YUV420P
-  && m_pCodecContext->pix_fmt != PIX_FMT_YUVJ420P
-  && m_pHardware == NULL)
+  if(m_pHardware == NULL)
   {
-    if (!m_dllSwScale.IsLoaded() && !m_dllSwScale.Load())
-        return VC_ERROR;
-
-    if (!m_pConvertFrame)
-    {
-      // Allocate an AVFrame structure
-      m_pConvertFrame = (AVPicture*)m_dllAvUtil.av_mallocz(sizeof(AVPicture));
-      // Due to a bug in swsscale we need to allocate one extra line of data
-      if(m_dllAvCodec.avpicture_alloc( m_pConvertFrame
-                                     , PIX_FMT_YUV420P
-                                     , m_pCodecContext->width
-                                     , m_pCodecContext->height+1) < 0)
-      {
-        m_dllAvUtil.av_free(m_pConvertFrame);
-        m_pConvertFrame = NULL;
-        return VC_ERROR;
-      }
-    }
+    bool need_scale = std::find( m_formats.begin()
+                               , m_formats.end()
+                               , m_pCodecContext->pix_fmt) == m_formats.end();
 
-    // convert the picture
-    struct SwsContext *context = m_dllSwScale.sws_getContext(m_pCodecContext->width, m_pCodecContext->height,
-                                         m_pCodecContext->pix_fmt, m_pCodecContext->width, m_pCodecContext->height,
-                                         PIX_FMT_YUV420P, SWS_FAST_BILINEAR | SwScaleCPUFlags(), NULL, NULL, NULL);
+    bool need_reopen  = false;
+    if(!m_filters.Equals(m_filters_next))
+      need_reopen = true;
 
-    if(context == NULL)
+    if(m_pFilterIn)
     {
-      CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::Decode - unable to obtain sws context for w:%i, h:%i, pixfmt: %i", m_pCodecContext->width, m_pCodecContext->height, m_pCodecContext->pix_fmt);
-      return VC_ERROR;
+      if(m_pFilterIn->outputs[0]->format != m_pCodecContext->pix_fmt
+      || m_pFilterIn->outputs[0]->w      != m_pCodecContext->width
+      || m_pFilterIn->outputs[0]->h      != m_pCodecContext->height)
+        need_reopen = true;
     }
 
-    m_dllSwScale.sws_scale(context
-                          , m_pFrame->data
-                          , m_pFrame->linesize
-                          , 0
-                          , m_pCodecContext->height
-                          , m_pConvertFrame->data
-                          , m_pConvertFrame->linesize);
-
-    m_dllSwScale.sws_freeContext(context);
-  }
-  else
-  {
-    // no need to convert, just free any existing convert buffers
-    if (m_pConvertFrame)
+    // try to setup new filters
+    if (need_reopen || (need_scale && m_pFilterGraph == NULL))
     {
-      m_dllAvCodec.avpicture_free(m_pConvertFrame);
-      m_dllAvUtil.av_free(m_pConvertFrame);
-      m_pConvertFrame = NULL;
-    }
-  }
+      m_filters = m_filters_next;
 
-  // try to setup new filters
-  if (!m_filters.Equals(m_filters_next))
-  {
-    m_filters = m_filters_next;
-    if(FilterOpen(m_filters) < 0)
-      FilterClose();
+      if(FilterOpen(m_filters, need_scale) < 0)
+        FilterClose();
+    }
   }
 
   int result;
@@ -564,12 +522,6 @@ void CDVDVideoCodecFFmpeg::Reset()
   if (m_pHardware)
     m_pHardware->Reset();
 
-  if (m_pConvertFrame)
-  {
-    m_dllAvCodec.avpicture_free(m_pConvertFrame);
-    m_dllAvUtil.av_free(m_pConvertFrame);
-    m_pConvertFrame = NULL;
-  }
   m_filters = "";
   FilterClose();
 }
@@ -579,10 +531,10 @@ bool CDVDVideoCodecFFmpeg::GetPictureCommon(DVDVideoPicture* pDvdVideoPicture)
   pDvdVideoPicture->iWidth = m_pCodecContext->width;
   pDvdVideoPicture->iHeight = m_pCodecContext->height;
 
-  if(m_pFilterLink)
+  if(m_pBufferRef)
   {
-    pDvdVideoPicture->iWidth  = m_pFilterLink->cur_buf->video->w;
-    pDvdVideoPicture->iHeight = m_pFilterLink->cur_buf->video->h;
+    pDvdVideoPicture->iWidth  = m_pBufferRef->video->w;
+    pDvdVideoPicture->iHeight = m_pBufferRef->video->h;
   }
 
   /* crop of 10 pixels if demuxer asked it */
@@ -598,12 +550,8 @@ bool CDVDVideoCodecFFmpeg::GetPictureCommon(DVDVideoPicture* pDvdVideoPicture)
 
   /* use variable in the frame */
   AVRational pixel_aspect = m_pCodecContext->sample_aspect_ratio;
-  if (m_pFilterLink)
-#ifdef HAVE_AVFILTERBUFFERREFVIDEOPROPS_SAMPLE_ASPECT_RATIO
-    pixel_aspect = m_pFilterLink->cur_buf->video->sample_aspect_ratio;
-#else
-    pixel_aspect = m_pFilterLink->cur_buf->video->pixel_aspect;
-#endif
+  if (m_pBufferRef)
+    pixel_aspect = m_pBufferRef->video->sample_aspect_ratio;
 
   if (pixel_aspect.num == 0)
     aspect_ratio = 0;
@@ -632,8 +580,6 @@ bool CDVDVideoCodecFFmpeg::GetPictureCommon(DVDVideoPicture* pDvdVideoPicture)
   pDvdVideoPicture->iFlags = DVP_FLAG_ALLOCATED;
   pDvdVideoPicture->iFlags |= m_pFrame->interlaced_frame ? DVP_FLAG_INTERLACED : 0;
   pDvdVideoPicture->iFlags |= m_pFrame->top_field_first ? DVP_FLAG_TOP_FIELD_FIRST: 0;
-  if(m_pCodecContext->pix_fmt == PIX_FMT_YUVJ420P)
-    pDvdVideoPicture->color_range = 1;
 
   pDvdVideoPicture->chroma_position = m_pCodecContext->chroma_sample_location;
   pDvdVideoPicture->color_primaries = m_pCodecContext->color_primaries;
@@ -677,14 +623,6 @@ bool CDVDVideoCodecFFmpeg::GetPicture(DVDVideoPicture* pDvdVideoPicture)
   if(!GetPictureCommon(pDvdVideoPicture))
     return false;
 
-  if(m_pConvertFrame)
-  {
-    for (int i = 0; i < 4; i++)
-      pDvdVideoPicture->data[i]      = m_pConvertFrame->data[i];
-    for (int i = 0; i < 4; i++)
-      pDvdVideoPicture->iLineSize[i] = m_pConvertFrame->linesize[i];
-  }
-  else
   {
     for (int i = 0; i < 4; i++)
       pDvdVideoPicture->data[i]      = m_pFrame->data[i];
@@ -693,20 +631,38 @@ bool CDVDVideoCodecFFmpeg::GetPicture(DVDVideoPicture* pDvdVideoPicture)
   }
 
   pDvdVideoPicture->iFlags |= pDvdVideoPicture->data[0] ? 0 : DVP_FLAG_DROPPED;
-  pDvdVideoPicture->format = DVDVideoPicture::FMT_YUV420P;
   pDvdVideoPicture->extended_format = 0;
+  pDvdVideoPicture->color_range = 0;
+
+  PixelFormat pix_fmt;
+  if(m_pBufferRef)
+    pix_fmt = (PixelFormat)m_pBufferRef->format;
+  else
+    pix_fmt = m_pCodecContext->pix_fmt;
+
+  switch(pix_fmt)
+  {
+    case PIX_FMT_YUVJ420P:
+      pDvdVideoPicture->format = DVDVideoPicture::FMT_YUV420P;
+      pDvdVideoPicture->color_range = 1;
+      break;
+    default:
+      pDvdVideoPicture->format = DVDVideoPicture::FMT_YUV420P;
+      break;
+  }
 
   return true;
 }
 
-int CDVDVideoCodecFFmpeg::FilterOpen(const CStdString& filters)
+int CDVDVideoCodecFFmpeg::FilterOpen(const CStdString& filters, bool scale)
 {
   int result;
+  AVBufferSinkParams *buffersink_params;
 
   if (m_pFilterGraph)
     FilterClose();
 
-  if (filters.IsEmpty())
+  if (filters.IsEmpty() && !scale)
     return 0;
 
   if (m_pHardware)
@@ -721,17 +677,8 @@ int CDVDVideoCodecFFmpeg::FilterOpen(const CStdString& filters)
     return -1;
   }
 
-  // CrHasher HACK (if an alternative becomes available use it!): In order to display the output
-  // produced by a combination of filters we insert "nullsink" as the last filter and we use
-  // its input pin as our output pin.
-  //
-  // input --> .. --> last_filter --> [in] nullsink [null]     [in] --> output
-  //                                   |                        |
-  //                                   |                        |
-  //                                   +------------------------+
-  //
   AVFilter* srcFilter = m_dllAvFilter.avfilter_get_by_name("buffer");
-  AVFilter* outFilter = m_dllAvFilter.avfilter_get_by_name("nullsink"); // should be last filter in the graph for now
+  AVFilter* outFilter = m_dllAvFilter.avfilter_get_by_name("buffersink"); // should be last filter in the graph for now
 
   CStdString args;
 
@@ -750,11 +697,19 @@ int CDVDVideoCodecFFmpeg::FilterOpen(const CStdString& filters)
     return result;
   }
 
-  if ((result = m_dllAvFilter.avfilter_graph_create_filter(&m_pFilterOut, outFilter, "out", NULL, NULL/*nullsink=>NULL*/, m_pFilterGraph)) < 0)
+  buffersink_params = m_dllAvFilter.av_buffersink_params_alloc();
+  buffersink_params->pixel_fmts = &m_formats[0];
+#ifdef FF_API_OLD_VSINK_API
+  if ((result = m_dllAvFilter.avfilter_graph_create_filter(&m_pFilterOut, outFilter, "out", NULL, (void*)buffersink_params->pixel_fmts, m_pFilterGraph)) < 0)
+#else
+  if ((result = m_dllAvFilter.avfilter_graph_create_filter(&m_pFilterOut, outFilter, "out", NULL, buffersink_params, m_pFilterGraph)) < 0)
+#endif
   {
+    m_dllAvUtil.av_freep(&buffersink_params);
     CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterOpen - avfilter_graph_create_filter: out");
     return result;
   }
+  m_dllAvUtil.av_freep(&buffersink_params);
 
   if (!filters.empty())
   {
@@ -800,6 +755,12 @@ int CDVDVideoCodecFFmpeg::FilterOpen(const CStdString& filters)
 
 void CDVDVideoCodecFFmpeg::FilterClose()
 {
+  if(m_pBufferRef)
+  {
+    m_dllAvFilter.avfilter_unref_buffer(m_pBufferRef);
+    m_pBufferRef = NULL;
+  }
+
   if (m_pFilterGraph)
   {
     m_dllAvFilter.avfilter_graph_free(&m_pFilterGraph);
@@ -807,7 +768,6 @@ void CDVDVideoCodecFFmpeg::FilterClose()
     // Disposed by above code
     m_pFilterIn   = NULL;
     m_pFilterOut  = NULL;
-    m_pFilterLink = NULL;
   }
 }
 
@@ -815,20 +775,9 @@ int CDVDVideoCodecFFmpeg::FilterProcess(AVFrame* frame)
 {
   int result, frames;
 
-  m_pFilterLink = m_pFilterOut->inputs[0];
-
   if (frame)
   {
-#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,13,0)
     result = m_dllAvFilter.av_vsrc_buffer_add_frame(m_pFilterIn, frame, 0);
-#elif LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(2,7,0)
-    result = m_dllAvFilter.av_vsrc_buffer_add_frame(m_pFilterIn, frame);
-#elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,3,0)
-    result = m_dllAvFilter.av_vsrc_buffer_add_frame(m_pFilterIn, frame, frame->pts);
-#else
-    result = m_dllAvFilter.av_vsrc_buffer_add_frame(m_pFilterIn, frame, frame->pts, m_pCodecContext->sample_aspect_ratio);
-#endif
-
     if (result < 0)
     {
       CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterProcess - av_vsrc_buffer_add_frame");
@@ -836,7 +785,13 @@ int CDVDVideoCodecFFmpeg::FilterProcess(AVFrame* frame)
     }
   }
 
-  if ((frames = m_dllAvFilter.avfilter_poll_frame(m_pFilterLink)) < 0)
+  if(m_pBufferRef)
+  {
+    m_dllAvFilter.avfilter_unref_buffer(m_pBufferRef);
+    m_pBufferRef = NULL;
+  }
+
+  if ((frames = m_dllAvFilter.av_buffersink_poll_frame(m_pFilterOut)) < 0)
   {
     CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterProcess - avfilter_poll_frame");
     return VC_ERROR;
@@ -844,19 +799,9 @@ int CDVDVideoCodecFFmpeg::FilterProcess(AVFrame* frame)
 
   if (frames > 0)
   {
-    if (m_pFilterLink->cur_buf)
-    {
-      m_dllAvFilter.avfilter_unref_buffer(m_pFilterLink->cur_buf);
-      m_pFilterLink->cur_buf = NULL;
-    }
-
-    if ((result = m_dllAvFilter.avfilter_request_frame(m_pFilterLink)) < 0)
-    {
-      CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterProcess - avfilter_request_frame");
-      return VC_ERROR;
-    }
 
-    if (!m_pFilterLink->cur_buf)
+    result = m_dllAvFilter.av_buffersink_get_buffer_ref(m_pFilterOut, &m_pBufferRef, 0);
+    if(!m_pBufferRef)
     {
       CLog::Log(LOGERROR, "CDVDVideoCodecFFmpeg::FilterProcess - cur_buf");
       return VC_ERROR;
@@ -867,11 +812,11 @@ int CDVDVideoCodecFFmpeg::FilterProcess(AVFrame* frame)
     else
       m_pFrame->repeat_pict      = -(frames - 1);
 
-    m_pFrame->interlaced_frame = m_pFilterLink->cur_buf->video->interlaced;
-    m_pFrame->top_field_first  = m_pFilterLink->cur_buf->video->top_field_first;
+    m_pFrame->interlaced_frame = m_pBufferRef->video->interlaced;
+    m_pFrame->top_field_first  = m_pBufferRef->video->top_field_first;
 
-    memcpy(m_pFrame->linesize, m_pFilterLink->cur_buf->linesize, 4*sizeof(int));
-    memcpy(m_pFrame->data    , m_pFilterLink->cur_buf->data    , 4*sizeof(uint8_t*));
+    memcpy(m_pFrame->linesize, m_pBufferRef->linesize, 4*sizeof(int));
+    memcpy(m_pFrame->data    , m_pBufferRef->data    , 4*sizeof(uint8_t*));
 
     if(frames > 1)
       return VC_PICTURE;