diff --git a/openbr/plugins/cmake/libav.cmake b/openbr/plugins/cmake/libav.cmake new file mode 100644 index 0000000..d2ddd4c --- /dev/null +++ b/openbr/plugins/cmake/libav.cmake @@ -0,0 +1,12 @@ +set(BR_WITH_LIBAV OFF CACHE BOOL "Build with LibAV") + +if(${BR_WITH_LIBAV}) + find_package(LibAV REQUIRED) + include_directories(${LIBAV_INCLUDE_DIR}) + set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${LIBAV_LIBRARIES}) + set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/gallery/keyframes.cpp) + + foreach(LIBAV_LIB ${LIBAV_LIBRARIES}) + install(FILES ${LIBAV_LIB} DESTINATION lib) + endforeach() +endif() diff --git a/openbr/plugins/gallery/keyframes.cpp b/openbr/plugins/gallery/keyframes.cpp new file mode 100644 index 0000000..b24807f --- /dev/null +++ b/openbr/plugins/gallery/keyframes.cpp @@ -0,0 +1,229 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright 2012 The MITRE Corporation * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +extern "C" +{ +#include +#include +#include +#include +} + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup galleries + * \brief Read key frames of a video with LibAV + * \author Ben Klein \cite bhklein + */ +class keyframesGallery : public Gallery +{ + Q_OBJECT + +public: + int64_t idx; + + keyframesGallery() + { + av_register_all(); + avformat_network_init(); + avFormatCtx = NULL; + avCodecCtx = NULL; + avSwsCtx = NULL; + avCodec = NULL; + frame = NULL; + cvt_frame = NULL; + buffer = NULL; + opened = false; + streamID = -1; + fps = 0.f; + time_base = 0.f; + idx = 0; + } + + ~keyframesGallery() + { + release(); + } + + virtual void deferredInit() + { + if (avformat_open_input(&avFormatCtx, QtUtils::getAbsolutePath(file.name).toStdString().c_str(), NULL, NULL) != 0) { + qFatal("Failed to open %s for reading.", qPrintable(file.name)); + } else if (avformat_find_stream_info(avFormatCtx, NULL) < 0) { + qFatal("Failed to read stream info for %s.", qPrintable(file.name)); + } else { + for (unsigned int i=0; inb_streams; i++) { + if (avFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + streamID = i; + break; + } + } + } + + if (streamID == -1) + qFatal("Failed to find video stream for %s", qPrintable(file.name)); + + avCodecCtx = avFormatCtx->streams[streamID]->codec; + avCodec = avcodec_find_decoder(avCodecCtx->codec_id); + if (avCodec == NULL) + qFatal("Unsupported codec for %s!", qPrintable(file.name)); + + if (avcodec_open2(avCodecCtx, avCodec, NULL) < 0) + qFatal("Could not open codec for file %s", qPrintable(file.name)); + + frame = av_frame_alloc(); + cvt_frame = av_frame_alloc(); + + // Get fps, stream time_base and allocate space for frame buffer with av_malloc. + fps = (float)avFormatCtx->streams[streamID]->avg_frame_rate.num / + (float)avFormatCtx->streams[streamID]->avg_frame_rate.den; + time_base = (float)avFormatCtx->streams[streamID]->time_base.num / + (float)avFormatCtx->streams[streamID]->time_base.den; + int framebytes = avpicture_get_size(AV_PIX_FMT_BGR24, avCodecCtx->width, avCodecCtx->height); + buffer = (uint8_t*)av_malloc(framebytes*sizeof(uint8_t)); + avpicture_fill((AVPicture*)cvt_frame, buffer, AV_PIX_FMT_BGR24, avCodecCtx->width, avCodecCtx->height); + + avSwsCtx = sws_getContext(avCodecCtx->width, avCodecCtx->height, + avCodecCtx->pix_fmt, + avCodecCtx->width, avCodecCtx->height, + AV_PIX_FMT_BGR24, + SWS_BICUBIC, + NULL, NULL, NULL); + + // attempt to seek to first keyframe + if (av_seek_frame(avFormatCtx, streamID, avFormatCtx->streams[streamID]->start_time, 0) < 0) + qFatal("Could not seek to beginning keyframe for %s!", qPrintable(file.name)); + avcodec_flush_buffers(avCodecCtx); + + opened = true; + } + + TemplateList readBlock(bool *done) + { + if (!opened) { + deferredInit(); + } + + Template output; + output.file = file; + + AVPacket packet; + av_init_packet(&packet); + int ret = 0; + while (!ret) { + if (av_read_frame(avFormatCtx, &packet) >= 0) { + if (packet.stream_index == streamID) { + avcodec_decode_video2(avCodecCtx, frame, &ret, &packet); + idx = packet.dts; // decompression timestamp + av_free_packet(&packet); + } else { + av_free_packet(&packet); + } + } else { + av_free_packet(&packet); + release(); + *done = true; + return TemplateList(); + } + } + + // Convert from native format + sws_scale(avSwsCtx, + frame->data, + frame->linesize, + 0, avCodecCtx->height, + cvt_frame->data, + cvt_frame->linesize); + + // Write AVFrame to cv::Mat + output.m() = Mat(avCodecCtx->height, avCodecCtx->width, CV_8UC3, cvt_frame->data[0]).clone(); + if (output.m().data) { + if (av_seek_frame(avFormatCtx, streamID, idx+1, 0) < 0) + *done = true; + avcodec_flush_buffers(avCodecCtx); + + QString URL = file.get("URL", file.name); + output.file.set("URL", URL + "#t=" + QString::number((int)(idx * time_base)) + "s"); + output.file.set("frame", QString::number(idx * time_base * fps)); + TemplateList dst; + dst.append(output); + return dst; + } + *done = true; + return TemplateList(); + } + + void release() + { + if (avSwsCtx) sws_freeContext(avSwsCtx); + if (frame) av_free(frame); + if (cvt_frame) av_free(cvt_frame); + if (avCodecCtx) avcodec_close(avCodecCtx); + if (avFormatCtx) avformat_close_input(&avFormatCtx); + if (buffer) av_free(buffer); + avFormatCtx = NULL; + avCodecCtx = NULL; + avSwsCtx = NULL; + avCodec = NULL; + frame = NULL; + cvt_frame = NULL; + buffer = NULL; + } + + void write(const Template &t) + { + (void)t; qFatal("Not implemented"); + } + +protected: + AVFormatContext *avFormatCtx; + AVCodecContext *avCodecCtx; + SwsContext *avSwsCtx; + AVCodec *avCodec; + AVFrame *frame; + AVFrame *cvt_frame; + uint8_t *buffer; + bool opened; + int streamID; + float fps; + float time_base; +}; + +BR_REGISTER(Gallery,keyframesGallery) + +/*! + * \ingroup galleries + * \brief Read key frames of a .mp4 video file with LibAV + * \author Ben Klein \cite bhklein + */ +class mp4Gallery : public keyframesGallery +{ + Q_OBJECT +}; + +BR_REGISTER(Gallery, mp4Gallery) + +} // namespace br + +#include "gallery/keyframes.moc" diff --git a/share/openbr/cmake/FindLibAV.cmake b/share/openbr/cmake/FindLibAV.cmake new file mode 100644 index 0000000..8aa31e1 --- /dev/null +++ b/share/openbr/cmake/FindLibAV.cmake @@ -0,0 +1,200 @@ +# Module for locating libav. +# +# Customizable variables: +# LIBAV_ROOT_DIR +# Specifies libav's root directory. +# +# Read-only variables: +# LIBAV_FOUND +# Indicates whether the library has been found. +# +# LIBAV_INCLUDE_DIRS +# Specifies libav's include directory. +# +# LIBAV_LIBRARIES +# Specifies libav libraries that should be passed to target_link_libararies. +# +# LIBAV__LIBRARIES +# Specifies the libraries of a specific . +# +# LIBAV__FOUND +# Indicates whether the specified was found. +# +# +# Copyright (c) 2012 Sergiu Dotenco +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTLIBAVLAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +INCLUDE (FindPackageHandleStandardArgs) + +IF (CMAKE_VERSION VERSION_GREATER 2.8.7) + SET (_LIBAV_CHECK_COMPONENTS FALSE) +ELSE (CMAKE_VERSION VERSION_GREATER 2.8.7) + SET (_LIBAV_CHECK_COMPONENTS TRUE) +ENDIF (CMAKE_VERSION VERSION_GREATER 2.8.7) + +FIND_PATH (LIBAV_ROOT_DIR + NAMES include/libavcodec/avcodec.h + include/libavdevice/avdevice.h + include/libavfilter/avfilter.h + include/libavutil/avutil.h + include/libswscale/swscale.h + PATHS ENV LIBAVROOT + DOC "libav root directory") + +FIND_PATH (LIBAV_INCLUDE_DIR + NAMES libavcodec/avcodec.h + libavdevice/avdevice.h + libavfilter/avfilter.h + libavutil/avutil.h + libswscale/swscale.h + HINTS ${LIBAV_ROOT_DIR} + PATH_SUFFIXES include + DOC "libav include directory") + +if (NOT LibAV_FIND_COMPONENTS) + set (LibAV_FIND_COMPONENTS avcodec avdevice avfilter avformat avutil swscale) +endif (NOT LibAV_FIND_COMPONENTS) + +FOREACH (_LIBAV_COMPONENT ${LibAV_FIND_COMPONENTS}) + STRING (TOUPPER ${_LIBAV_COMPONENT} _LIBAV_COMPONENT_UPPER) + SET (_LIBAV_LIBRARY_BASE LIBAV_${_LIBAV_COMPONENT_UPPER}_LIBRARY) + + FIND_LIBRARY (${_LIBAV_LIBRARY_BASE} + NAMES ${_LIBAV_COMPONENT} + HINTS ${LIBAV_ROOT_DIR} + PATH_SUFFIXES bin lib + DOC "libav ${_LIBAV_COMPONENT} library") + + MARK_AS_ADVANCED (${_LIBAV_LIBRARY_BASE}) + + SET (LIBAV_${_LIBAV_COMPONENT_UPPER}_FOUND TRUE) + SET (LibAV_${_LIBAV_COMPONENT}_FOUND ${LIBAV_${_LIBAV_COMPONENT_UPPER}_FOUND}) + + IF (${_LIBAV_LIBRARY_BASE}) + # setup the LIBAV__LIBRARIES variable + SET (LIBAV_${_LIBAV_COMPONENT_UPPER}_LIBRARIES ${${_LIBAV_LIBRARY_BASE}}) + LIST (APPEND LIBAV_LIBRARIES ${LIBAV_${_LIBAV_COMPONENT_UPPER}_LIBRARIES}) + LIST (APPEND _LIBAV_ALL_LIBS ${${_LIBAV_LIBRARY_BASE}}) + ELSE (${_LIBAV_LIBRARY_BASE}) + LIST (APPEND _LIBAV_MISSING_LIBRARIES ${_LIBAV_LIBRARY_BASE}) + ENDIF (${_LIBAV_LIBRARY_BASE}) +ENDFOREACH (_LIBAV_COMPONENT ${LibAV_FIND_COMPONENTS}) + +SET (LIBAV_INCLUDE_DIRS ${LIBAV_INCLUDE_DIR}) + +IF (DEFINED _LIBAV_MISSING_COMPONENTS AND _LIBAV_CHECK_COMPONENTS) + IF (NOT LibAV_FIND_QUIETLY) + MESSAGE (STATUS "One or more libav components were not found:") + # Display missing components indented, each on a separate line + FOREACH (_LIBAV_MISSING_COMPONENT ${_LIBAV_MISSING_COMPONENTS}) + MESSAGE (STATUS " " ${_LIBAV_MISSING_COMPONENT}) + ENDFOREACH (_LIBAV_MISSING_COMPONENT ${_LIBAV_MISSING_COMPONENTS}) + ENDIF (NOT LibAV_FIND_QUIETLY) +ENDIF (DEFINED _LIBAV_MISSING_COMPONENTS AND _LIBAV_CHECK_COMPONENTS) + +# Determine library's version + +FIND_PROGRAM (LIBAV_AVCONV_EXECUTABLE NAMES avconv + HINTS ${LIBAV_ROOT_DIR} + PATH_SUFFIXES bin + DOC "avconv executable") + +IF (LIBAV_AVCONV_EXECUTABLE) + EXECUTE_PROCESS (COMMAND ${LIBAV_AVCONV_EXECUTABLE} -version + OUTPUT_VARIABLE _LIBAV_AVCONV_OUTPUT ERROR_QUIET) + + STRING (REGEX REPLACE ".*avconv[ \t]+([0-9]+\\.[0-9]+\\.[0-9]+).*" "\\1" + LIBAV_VERSION "${_LIBAV_AVCONV_OUTPUT}") + STRING (REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\1" + LIBAV_VERSION_MAJOR "${LIBAV_VERSION}") + STRING (REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\2" + LIBAV_VERSION_MINOR "${LIBAV_VERSION}") + STRING (REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\3" + LIBAV_VERSION_PATCH "${LIBAV_VERSION}") + + SET (LIBAV_VERSION_COMPONENTS 3) +ENDIF (LIBAV_AVCONV_EXECUTABLE) + +IF (WIN32) + FIND_PROGRAM (LIB_EXECUTABLE NAMES lib + HINTS "$ENV{VS110COMNTOOLS}/../../VC/bin" + "$ENV{VS100COMNTOOLS}/../../VC/bin" + "$ENV{VS90COMNTOOLS}/../../VC/bin" + "$ENV{VS71COMNTOOLS}/../../VC/bin" + "$ENV{VS80COMNTOOLS}/../../VC/bin" + DOC "Library manager") + + MARK_AS_ADVANCED (LIB_EXECUTABLE) +ENDIF (WIN32) + +MACRO (GET_LIB_REQUISITES LIB REQUISITES) + IF (LIB_EXECUTABLE) + GET_FILENAME_COMPONENT (_LIB_PATH ${LIB_EXECUTABLE} PATH) + + EXECUTE_PROCESS (COMMAND ${LIB_EXECUTABLE} /nologo /list ${LIB} + WORKING_DIRECTORY ${_LIB_PATH}/../../Common7/IDE + OUTPUT_VARIABLE _LIB_OUTPUT ERROR_QUIET) + + STRING (REPLACE "\n" ";" "${REQUISITES}" "${_LIB_OUTPUT}") + LIST (REMOVE_DUPLICATES ${REQUISITES}) + ENDIF (LIB_EXECUTABLE) +ENDMACRO (GET_LIB_REQUISITES) + +IF (_LIBAV_ALL_LIBS) + # collect lib requisites using the lib tool + FOREACH (_LIBAV_COMPONENT ${_LIBAV_ALL_LIBS}) + GET_LIB_REQUISITES (${_LIBAV_COMPONENT} _LIBAV_REQUISITES) + ENDFOREACH (_LIBAV_COMPONENT) +ENDIF (_LIBAV_ALL_LIBS) + +IF (NOT LIBAV_BINARY_DIR) + SET (_LIBAV_UPDATE_BINARY_DIR TRUE) +ELSE (NOT LIBAV_BINARY_DIR) + SET (_LIBAV_UPDATE_BINARY_DIR FALSE) +ENDIF (NOT LIBAV_BINARY_DIR) + +SET (_LIBAV_BINARY_DIR_HINTS bin) + +IF (_LIBAV_REQUISITES) + FIND_FILE (LIBAV_BINARY_DIR NAMES ${_LIBAV_REQUISITES} + HINTS ${LIBAV_ROOT_DIR} + PATH_SUFFIXES ${_LIBAV_BINARY_DIR_HINTS} NO_DEFAULT_PATH) +ENDIF (_LIBAV_REQUISITES) + +IF (LIBAV_BINARY_DIR AND _LIBAV_UPDATE_BINARY_DIR) + SET (_LIBAV_BINARY_DIR ${LIBAV_BINARY_DIR}) + UNSET (LIBAV_BINARY_DIR CACHE) + + IF (_LIBAV_BINARY_DIR) + GET_FILENAME_COMPONENT (LIBAV_BINARY_DIR ${_LIBAV_BINARY_DIR} PATH) + ENDIF (_LIBAV_BINARY_DIR) +ENDIF (LIBAV_BINARY_DIR AND _LIBAV_UPDATE_BINARY_DIR) + +SET (LIBAV_BINARY_DIR ${LIBAV_BINARY_DIR} CACHE PATH "libav binary directory") + +MARK_AS_ADVANCED (LIBAV_INCLUDE_DIR LIBAV_BINARY_DIR) + +IF (NOT _LIBAV_CHECK_COMPONENTS) + SET (_LIBAV_FPHSA_ADDITIONAL_ARGS HANDLE_COMPONENTS) +ENDIF (NOT _LIBAV_CHECK_COMPONENTS) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS (LibAV REQUIRED_VARS LIBAV_ROOT_DIR + LIBAV_INCLUDE_DIR ${_LIBAV_MISSING_LIBRARIES} VERSION_VAR LIBAV_VERSION + ${_LIBAV_FPHSA_ADDITIONAL_ARGS}) \ No newline at end of file