From 3978c762072b7bc16b2096819b7cfa2052deaf5e Mon Sep 17 00:00:00 2001 From: "Martin T. H. Sandsmark" Date: Fri, 27 May 2016 22:29:11 +0200 Subject: [PATCH] Port to libavfilter for deinterlacing. Based on a patch from Andreas Cadhalpun . --- CMakeLists.txt | 2 +- cmake/FindFFmpeg.cmake | 1 + ffmpegthumbnailer/moviedecoder.cpp | 117 ++++++++++++++++++++++++++--- ffmpegthumbnailer/moviedecoder.h | 18 ++++- tests/CMakeLists.txt | 2 +- 5 files changed, 124 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 48eed64..3c761fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ set( ffmpegthumbs_PART_SRCS add_library(ffmpegthumbs MODULE ${ffmpegthumbs_PART_SRCS}) -target_link_libraries(ffmpegthumbs Qt5::Gui KF5::KIOWidgets ${AVUTIL_LIBRARIES} ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${SWSCALE_LIBRARIES} ) +target_link_libraries(ffmpegthumbs Qt5::Gui KF5::KIOWidgets ${AVUTIL_LIBRARIES} ${AVFILTER_LIBRARIES} ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${SWSCALE_LIBRARIES} ) install(TARGETS ffmpegthumbs DESTINATION ${PLUGIN_INSTALL_DIR}) diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake index 27c6b16..915ab5f 100644 --- a/cmake/FindFFmpeg.cmake +++ b/cmake/FindFFmpeg.cmake @@ -99,6 +99,7 @@ if (NOT FFMPEG_LIBRARIES) # Check for all possible component. find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h) + find_component(AVFILTER libavfilter avfilter libavfilter/avfilter.h) find_component(AVFORMAT libavformat avformat libavformat/avformat.h) find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h) find_component(AVUTIL libavutil avutil libavutil/avutil.h) diff --git a/ffmpegthumbnailer/moviedecoder.cpp b/ffmpegthumbnailer/moviedecoder.cpp index c8ae6d1..b2e7551 100644 --- a/ffmpegthumbnailer/moviedecoder.cpp +++ b/ffmpegthumbnailer/moviedecoder.cpp @@ -40,6 +40,10 @@ MovieDecoder::MovieDecoder(const QString& filename, AVFormatContext* pavContext) , m_FormatContextWasGiven(pavContext != NULL) , m_AllowSeek(true) , m_initialized(false) + , m_bufferSinkContext(nullptr) + , m_bufferSourceContext(nullptr) + , m_filterGraph(nullptr) + , m_filterFrame(nullptr) { initialize(filename); } @@ -51,6 +55,9 @@ MovieDecoder::~MovieDecoder() void MovieDecoder::initialize(const QString& filename) { + m_lastWidth = -1; + m_lastHeight = -1; + m_lastPixfmt = AV_PIX_FMT_NONE; av_register_all(); avcodec_register_all(); @@ -67,7 +74,7 @@ void MovieDecoder::initialize(const QString& filename) } initializeVideo(); - m_pFrame = avcodec_alloc_frame(); + m_pFrame = av_frame_alloc(); if (m_pFrame) { m_initialized=true; @@ -82,6 +89,7 @@ bool MovieDecoder::getInitialized() void MovieDecoder::destroy() { + deleteFilterGraph(); if (m_pVideoCodecContext) { avcodec_close(m_pVideoCodecContext); m_pVideoCodecContext = NULL; @@ -93,13 +101,13 @@ void MovieDecoder::destroy() } if (m_pPacket) { - av_free_packet(m_pPacket); + av_packet_unref(m_pPacket); delete m_pPacket; m_pPacket = NULL; } if (m_pFrame) { - av_free(m_pFrame); + av_frame_free(&m_pFrame); m_pFrame = NULL; } @@ -239,7 +247,7 @@ bool MovieDecoder::decodeVideoPacket() return false; } - avcodec_get_frame_defaults(m_pFrame); + av_frame_unref(m_pFrame); int frameFinished = 0; @@ -264,7 +272,7 @@ bool MovieDecoder::getVideoPacket() int attempts = 0; if (m_pPacket) { - av_free_packet(m_pPacket); + av_packet_unref(m_pPacket); delete m_pPacket; } @@ -275,7 +283,7 @@ bool MovieDecoder::getVideoPacket() if (framesAvailable) { frameDecoded = m_pPacket->stream_index == m_VideoStream; if (!frameDecoded) { - av_free_packet(m_pPacket); + av_packet_unref(m_pPacket); } } } @@ -283,15 +291,100 @@ bool MovieDecoder::getVideoPacket() return frameDecoded; } +void MovieDecoder::deleteFilterGraph() +{ + if (m_filterGraph) { + av_frame_free(&m_filterFrame); + avfilter_graph_free(&m_filterGraph); + m_filterGraph = nullptr; + } +} + +bool MovieDecoder::initFilterGraph(enum AVPixelFormat pixfmt, int width, int height) +{ + AVFilterInOut *inputs = nullptr, *outputs = nullptr; + + deleteFilterGraph(); + m_filterGraph = avfilter_graph_alloc(); + + QByteArray arguments("buffer="); + arguments += "video_size=" + QByteArray::number(width) + "x" + QByteArray::number(height) + ":"; + arguments += "pix_fmt=" + QByteArray::number(pixfmt) + ":"; + arguments += "time_base=1/1:pixel_aspect=0/1[in];"; + arguments += "[in]yadif[out];"; + arguments += "[out]buffersink"; + + int ret = avfilter_graph_parse2(m_filterGraph, arguments.constData(), &inputs, &outputs); + if (ret < 0) { + qWarning() << "Unable to parse filter graph"; + return false; + } + + if(inputs || outputs) + return -1; + + ret = avfilter_graph_config(m_filterGraph, nullptr); + if (ret < 0) { + qWarning() << "Unable to validate filter graph"; + return false; + } + + m_bufferSourceContext = avfilter_graph_get_filter(m_filterGraph, "Parsed_buffer_0"); + m_bufferSinkContext = avfilter_graph_get_filter(m_filterGraph, "Parsed_buffersink_2"); + if (!m_bufferSourceContext || !m_bufferSinkContext) { + qWarning() << "Unable to get source or sink"; + return false; + } + m_filterFrame = av_frame_alloc(); + m_lastWidth = width; + m_lastHeight = height; + m_lastPixfmt = pixfmt; + + return true; +} + +bool MovieDecoder::processFilterGraph(AVPicture *dst, const AVPicture *src, + enum AVPixelFormat pixfmt, int width, int height) +{ + if (!m_filterGraph || width != m_lastWidth || + height != m_lastHeight || pixfmt != m_lastPixfmt) { + + if (!initFilterGraph(pixfmt, width, height)) { + return false; + } + } + + memcpy(m_filterFrame->data, src->data, sizeof(src->data)); + memcpy(m_filterFrame->linesize, src->linesize, sizeof(src->linesize)); + m_filterFrame->width = width; + m_filterFrame->height = height; + m_filterFrame->format = pixfmt; + + int ret = av_buffersrc_add_frame(m_bufferSourceContext, m_filterFrame); + if (ret < 0) { + return false; + } + + ret = av_buffersink_get_frame(m_bufferSinkContext, m_filterFrame); + if (ret < 0) { + return false; + } + + av_picture_copy(dst, (const AVPicture *) m_filterFrame, pixfmt, width, height); + av_frame_unref(m_filterFrame); + + return true; +} + void MovieDecoder::getScaledVideoFrame(int scaledSize, bool maintainAspectRatio, VideoFrame& videoFrame) { if (m_pFrame->interlaced_frame) { - avpicture_deinterlace((AVPicture*) m_pFrame, (AVPicture*) m_pFrame, m_pVideoCodecContext->pix_fmt, + processFilterGraph((AVPicture*) m_pFrame, (AVPicture*) m_pFrame, m_pVideoCodecContext->pix_fmt, m_pVideoCodecContext->width, m_pVideoCodecContext->height); } int scaledWidth, scaledHeight; - convertAndScaleFrame(PIX_FMT_RGB24, scaledSize, maintainAspectRatio, scaledWidth, scaledHeight); + convertAndScaleFrame(AV_PIX_FMT_RGB24, scaledSize, maintainAspectRatio, scaledWidth, scaledHeight); videoFrame.width = scaledWidth; videoFrame.height = scaledHeight; @@ -302,7 +395,7 @@ void MovieDecoder::getScaledVideoFrame(int scaledSize, bool maintainAspectRatio, memcpy((&(videoFrame.frameData.front())), m_pFrame->data[0], videoFrame.lineSize * videoFrame.height); } -void MovieDecoder::convertAndScaleFrame(PixelFormat format, int scaledSize, bool maintainAspectRatio, int& scaledWidth, int& scaledHeight) +void MovieDecoder::convertAndScaleFrame(AVPixelFormat format, int scaledSize, bool maintainAspectRatio, int& scaledWidth, int& scaledHeight) { calculateDimensions(scaledSize, maintainAspectRatio, scaledWidth, scaledHeight); SwsContext* scaleContext = sws_getContext(m_pVideoCodecContext->width, m_pVideoCodecContext->height, @@ -323,7 +416,7 @@ void MovieDecoder::convertAndScaleFrame(PixelFormat format, int scaledSize, bool convertedFrame->data, convertedFrame->linesize); sws_freeContext(scaleContext); - av_free(m_pFrame); + av_frame_free(&m_pFrame); av_free(m_pFrameBuffer); m_pFrame = convertedFrame; @@ -355,9 +448,9 @@ void MovieDecoder::calculateDimensions(int squareSize, bool maintainAspectRatio, } } -void MovieDecoder::createAVFrame(AVFrame** avFrame, quint8** frameBuffer, int width, int height, PixelFormat format) +void MovieDecoder::createAVFrame(AVFrame** avFrame, quint8** frameBuffer, int width, int height, AVPixelFormat format) { - *avFrame = avcodec_alloc_frame(); + *avFrame = av_frame_alloc(); int numBytes = avpicture_get_size(format, width, height); *frameBuffer = reinterpret_cast(av_malloc(numBytes)); diff --git a/ffmpegthumbnailer/moviedecoder.h b/ffmpegthumbnailer/moviedecoder.h index 2888926..788ce43 100644 --- a/ffmpegthumbnailer/moviedecoder.h +++ b/ffmpegthumbnailer/moviedecoder.h @@ -23,6 +23,9 @@ extern "C" { #include #include +#include +#include +#include } namespace ffmpegthumbnailer @@ -52,10 +55,14 @@ private: bool decodeVideoPacket(); bool getVideoPacket(); - void convertAndScaleFrame(PixelFormat format, int scaledSize, bool maintainAspectRatio, int& scaledWidth, int& scaledHeight); - void createAVFrame(AVFrame** avFrame, quint8** frameBuffer, int width, int height, PixelFormat format); + void convertAndScaleFrame(AVPixelFormat format, int scaledSize, bool maintainAspectRatio, int& scaledWidth, int& scaledHeight); + void createAVFrame(AVFrame** avFrame, quint8** frameBuffer, int width, int height, AVPixelFormat format); void calculateDimensions(int squareSize, bool maintainAspectRatio, int& destWidth, int& destHeight); + void deleteFilterGraph(); + bool initFilterGraph(enum AVPixelFormat pixfmt, int width, int height); + bool processFilterGraph(AVPicture *dst, const AVPicture *src, enum AVPixelFormat pixfmt, int width, int height); + private: int m_VideoStream; AVFormatContext* m_pFormatContext; @@ -68,6 +75,13 @@ private: bool m_FormatContextWasGiven; bool m_AllowSeek; bool m_initialized; + AVFilterContext* m_bufferSinkContext; + AVFilterContext* m_bufferSourceContext; + AVFilterGraph* m_filterGraph; + AVFrame* m_filterFrame; + int m_lastWidth; + int m_lastHeight; + enum AVPixelFormat m_lastPixfmt; }; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1728fcd..b46169a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,7 +19,7 @@ set(ffmpegthumbtest_SRCS ffmpegthumbtest.cpp add_executable(ffmpegthumbtest ${ffmpegthumbtest_SRCS} ) -target_link_libraries(ffmpegthumbtest Qt5::Gui KF5::KIOWidgets ${AVUTIL_LIBRARIES} ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${SWSCALE_LIBRARIES}) +target_link_libraries(ffmpegthumbtest Qt5::Gui KF5::KIOWidgets ${AVUTIL_LIBRARIES} ${AVFILTER_LIBRARIES} ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${SWSCALE_LIBRARIES}) -- GitLab