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;
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)
{
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;
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();
}
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
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;
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;
}
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);
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;
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;
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();
}
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 */
/* 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;
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;
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];
}
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)
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;
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())
{
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);
// Disposed by above code
m_pFilterIn = NULL;
m_pFilterOut = NULL;
- m_pFilterLink = NULL;
}
}
{
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");
}
}
- 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;
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;
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;