├── .gitignore ├── CMakeLists.txt ├── README.md ├── cmake └── FindFFMPEG.cmake ├── generic └── thffmpeg.c ├── init.lua ├── test ├── test.lua └── ucf101_icedancing.avi ├── thffmpeg-1.0.alpha-0.rockspec └── thffmpeg.c /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | old 3 | build -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.6 FATAL_ERROR) 2 | CMAKE_POLICY(VERSION 2.6) 3 | 4 | SET(CMAKE_MODULE_PATH 5 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake" 6 | "${CMAKE_MODULE_PATH}") 7 | 8 | FIND_PACKAGE(Torch REQUIRED) 9 | FIND_PACKAGE(FFMPEG REQUIRED) 10 | 11 | SET(src thffmpeg.c) 12 | SET(luasrc init.lua test/test.lua) 13 | 14 | ADD_TORCH_PACKAGE(thffmpeg "${src}" "${luasrc}" "Video reader") 15 | TARGET_LINK_LIBRARIES(thffmpeg luaT TH avformat swscale avcodec) 16 | INCLUDE_DIRECTORIES(${FFMPEG_INCLUDE_DIR}) 17 | INSTALL(FILES "README.md" DESTINATION "${Torch_INSTALL_LUA_PATH_SUBDIR}/thffmpeg") 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THFFmpeg 2 | 3 | Some (quick and not so dirty) bindings of ffmpeg for torch. 4 | Big thanks to https://github.com/mpenkov/ffmpeg-tutorial for their tutorials. 5 | 6 | Under BSD License. 7 | 8 | ## Install 9 | 10 | It requires ffmpeg 2.x (tested with 2.8.5) for now. 11 | 12 | Clone this git, then type 13 | ``` 14 | luarocks make 15 | ``` 16 | 17 | ### Troubleshooting 18 | 19 | When installing, I had a problem with the version of ffmpeg (the headers version didn't match the loaded library). 20 | There is now a check at initialization. If it fails, make sure that you compile with the same headers. 21 | A way to force them is 22 | ``` 23 | luarocks make 24 | cd build 25 | ccmake . 26 | # set the correct paths 27 | cd .. 28 | luarocks make 29 | ``` 30 | After that, as long as you don't remove the `build` folder, it should compile fine with the future `luarocks make` calls. 31 | 32 | The only way I found to set the correct paths were to add `-I/usr/include/ffmpeg` to the CXX and C flags, in `ccmake .`. 33 | This should work on NYU servers, but other paths may be necessary depending on where you installed `ffmpeg`. 34 | 35 | ### Testing 36 | 37 | To test the install, you can run 38 | ``` 39 | th test/test.th 40 | ``` 41 | 42 | ## Usage 43 | 44 | In torch: 45 | ``` 46 | require 'thffmpeg' 47 | 48 | decoder = THFFmpeg() --creates a decoder object 49 | decoder:open('myvideo.avi') --opens the video myvideo.avi 50 | frame1 = decoder:next_frame() --allocates a new tensor and puts the first frame in it 51 | frame2 = torch.Tensor(42,42) 52 | decoder:next_frame(frame2) --resizes frame2 if necessary and puts the second frame in it 53 | decoder:close() --closes the video 54 | decoder:open('myvideo2.avi') --opens the video myvideo2.avi 55 | ``` 56 | 57 | ### THFFmpeg:open(filename) 58 | 59 | Opens a video file. If another was opened in the same THFFmpeg object, it is closed. 60 | It sets the variables `h` and `w` of the THFFmpeg object to the size of the video stream. 61 | 62 | ### THFFmpeg:close() 63 | 64 | Closes a video file. It is not usually strictly necessary, as the decoder will 65 | automatically close the opened video when another video is opened, 66 | or when the decoder is garbage collected. 67 | 68 | ### [res] THFFmpeg:next_frame([dst]) 69 | 70 | Decodes and returns the next frame. If `dst` is provided, it is used as a destination buffer, 71 | and must have the correct size. 72 | This function returns nil when there is no more frames to read. 73 | 74 | ### [res] THFFmpeg:skip_frame() 75 | 76 | Skips a frame. Returns true if successful and false otherwise. Because the frame is not processed, it 77 | is faster than next_frame. 78 | 79 | ### [res] THFFmpeg:seek(idx) 80 | 81 | Seeks the `idx`-th frame. Because of limitations of video format, this function decodes 82 | every frame from the beginning, so it might be slow on large videos. However, it will 83 | always be faster than calling `idx` times the function `next_frame` because the 84 | images are not processed. 85 | This function returns `true` if the seek was a success, and `false` otherwise. 86 | 87 | ### [res] THFFmpeg:length() 88 | 89 | Returns the length of the video. As for `seek`, this function decodes all frames of the 90 | video and therefore might be slow on large videos. 91 | -------------------------------------------------------------------------------- /cmake/FindFFMPEG.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil) 2 | # Once done this will define 3 | # 4 | # FFMPEG_FOUND - system has ffmpeg or libav 5 | # FFMPEG_INCLUDE_DIR - the ffmpeg include directory 6 | # FFMPEG_LIBRARIES - Link these to use ffmpeg 7 | # FFMPEG_LIBAVCODEC 8 | # FFMPEG_LIBAVFORMAT 9 | # FFMPEG_LIBAVUTIL 10 | # 11 | # Copyright (c) 2008 Andreas Schneider 12 | # Modified for other libraries by Lasse Kärkkäinen 13 | # Modified for Hedgewars by Stepik777 14 | # 15 | # Redistribution and use is allowed according to the terms of the New 16 | # BSD license. 17 | # 18 | 19 | if (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) 20 | # in cache already 21 | set(FFMPEG_FOUND TRUE) 22 | else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) 23 | # use pkg-config to get the directories and then use these values 24 | # in the FIND_PATH() and FIND_LIBRARY() calls 25 | find_package(PkgConfig) 26 | if (PKG_CONFIG_FOUND) 27 | pkg_check_modules(_FFMPEG_AVCODEC libavcodec) 28 | pkg_check_modules(_FFMPEG_AVFORMAT libavformat) 29 | pkg_check_modules(_FFMPEG_AVUTIL libavutil) 30 | endif (PKG_CONFIG_FOUND) 31 | 32 | find_path(FFMPEG_AVCODEC_INCLUDE_DIR 33 | NAMES libavcodec/avcodec.h 34 | PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS} /usr/include /usr/local/include /opt/local/include /sw/include 35 | PATH_SUFFIXES ffmpeg libav 36 | ) 37 | 38 | find_library(FFMPEG_LIBAVCODEC 39 | NAMES avcodec 40 | PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib 41 | ) 42 | 43 | find_library(FFMPEG_LIBAVFORMAT 44 | NAMES avformat 45 | PATHS ${_FFMPEG_AVFORMAT_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib 46 | ) 47 | 48 | find_library(FFMPEG_LIBAVUTIL 49 | NAMES avutil 50 | PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib 51 | ) 52 | 53 | if (FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT) 54 | set(FFMPEG_FOUND TRUE) 55 | endif() 56 | 57 | if (FFMPEG_FOUND) 58 | set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR}) 59 | 60 | set(FFMPEG_LIBRARIES 61 | ${FFMPEG_LIBAVCODEC} 62 | ${FFMPEG_LIBAVFORMAT} 63 | ${FFMPEG_LIBAVUTIL} 64 | ) 65 | 66 | endif (FFMPEG_FOUND) 67 | 68 | if (FFMPEG_FOUND) 69 | if (NOT FFMPEG_FIND_QUIETLY) 70 | message(STATUS "Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}") 71 | endif (NOT FFMPEG_FIND_QUIETLY) 72 | else (FFMPEG_FOUND) 73 | if (FFMPEG_FIND_REQUIRED) 74 | message(FATAL_ERROR "Could not find libavcodec or libavformat or libavutil") 75 | endif (FFMPEG_FIND_REQUIRED) 76 | endif (FFMPEG_FOUND) 77 | 78 | endif (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) -------------------------------------------------------------------------------- /generic/thffmpeg.c: -------------------------------------------------------------------------------- 1 | #ifndef TH_GENERIC_FILE 2 | #define TH_GENERIC_FILE "generic/thffmpeg.c" 3 | #else 4 | 5 | int thffmpeg_(Main_avreadframe)(lua_State* L) { 6 | AV_Struct* avs = (AV_Struct*)lua_touserdata(L, 1); 7 | THTensor* t = luaT_checkudata(L, 2, torch_Tensor); 8 | luaL_argcheck(L, avs->pFrameRGB != NULL, 1, "No video has been opened"); 9 | luaL_argcheck(L, t->nDimension == 3, 2, "Dst must be a 3D tensor"); 10 | luaL_argcheck(L, THTensor_(isContiguous)(t), 2, "Dst must be contiguous"); 11 | luaL_argcheck(L, t->size[0] == 3, 2, "Dst 1st dim must be 3"); 12 | luaL_argcheck(L, (t->size[1] == avs->h) && (t->size[2] == avs->w), 13 | 2, "Dst tensor has wrong size"); 14 | AVFrame* frame = AV_read_frame(avs); 15 | if (frame == NULL) { 16 | lua_pushboolean(L, 0); 17 | } else { 18 | int x, y; 19 | real* dstData = THTensor_(data)(t); 20 | for (y = 0; y < avs->h; ++y) { 21 | for (x = 0; x < avs->w; ++x) { 22 | uint8_t* p = frame->data[0] + y * frame->linesize[0] + x * 3; 23 | dstData[ y * avs->w + x] = p[0]; 24 | dstData[( avs->h + y) * avs->w + x] = p[1]; 25 | dstData[(2 * avs->h + y) * avs->w + x] = p[2]; 26 | //THTensor_(set3d)(t, 0, y, x, (real)p[0]); 27 | //THTensor_(set3d)(t, 1, y, x, (real)p[1]); 28 | //THTensor_(set3d)(t, 2, y, x, (real)p[2]); 29 | } 30 | } 31 | lua_pushboolean(L, 1); 32 | } 33 | return 1; 34 | } 35 | 36 | static const struct luaL_Reg thffmpeg_(Main__) [] = { 37 | {"readframe", thffmpeg_(Main_avreadframe)}, 38 | {NULL, NULL} 39 | }; 40 | 41 | void thffmpeg_(Main_init)(lua_State *L) { 42 | luaT_pushmetatable(L, torch_Tensor); 43 | luaT_registeratname(L, thffmpeg_(Main__), "libthffmpeg"); 44 | } 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Copyright (c) 2016, Michael Mathieu 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | The views and conclusions contained in the software and documentation are those 26 | of the authors and should not be interpreted as representing official policies, 27 | either expressed or implied, of the FreeBSD Project. 28 | --]] 29 | 30 | require 'torch' 31 | require 'paths' 32 | require 'libthffmpeg' 33 | 34 | local THFFmpeg = torch.class("THFFmpeg") 35 | 36 | --TODO cmakefile (libs) 37 | local global_ffmpeg_init = false 38 | function THFFmpeg:__init() 39 | self.avs = libthffmpeg.init(global_ffmpeg_init) 40 | global_ffmpeg_init = true 41 | end 42 | 43 | function THFFmpeg:open(filename) 44 | if not paths.filep(filename) then 45 | print("THFFmpeg: file " .. filename .. " does not exist") 46 | return false 47 | end 48 | self.h, self.w = libthffmpeg.open(self.avs, filename) 49 | return (self.h ~= nil) 50 | end 51 | 52 | function THFFmpeg:close() 53 | libthffmpeg.close(self.avs) 54 | end 55 | 56 | function THFFmpeg:next_frame(buffer) 57 | if self.h == nil then 58 | error("THFFmpeg: next_frame called without opening a video") 59 | end 60 | buffer = buffer or torch.Tensor(3, self.h, self.w) 61 | if (buffer:dim() ~= 3) or (buffer:size(1) ~= 3) 62 | or (buffer:size(2) ~= self.h) or (buffer:size(3) ~= self.w) then 63 | print("THFFmpeg: wrong buffer size: {buffer, self}") 64 | print{buffer, {3, self.h, self.w}} 65 | return nil 66 | end 67 | if buffer.libthffmpeg.readframe(self.avs, buffer) then 68 | return buffer 69 | else 70 | return nil 71 | end 72 | end 73 | 74 | function THFFmpeg:skip_frame() 75 | return libthffmpeg.skipframe(self.avs) 76 | end 77 | 78 | function THFFmpeg:seek(idx) 79 | return libthffmpeg.seek(self.avs, idx) 80 | end 81 | 82 | function THFFmpeg:length() 83 | return libthffmpeg.length(self.avs) 84 | end 85 | -------------------------------------------------------------------------------- /test/test.lua: -------------------------------------------------------------------------------- 1 | require 'thffmpeg' 2 | require 'sys' 3 | require 'math' 4 | 5 | x = THFFmpeg() 6 | y = THFFmpeg() 7 | x = nil 8 | collectgarbage() 9 | collectgarbage() 10 | 11 | if not y:open('v_ApplyEyeMakeup_g01_c01.avi') then 12 | print("Failed to open video") 13 | os.exit(0) 14 | end 15 | 16 | local fmeans = {87.119574652778, 87.233272569444, 86.159526909722} 17 | print(fmeans) 18 | 19 | require 'image' 20 | 21 | frame1 = y:next_frame() 22 | --image.display(frame1) 23 | --os.execute('sleep 10') 24 | print(frame1:mean()) 25 | 26 | if math.abs(frame1:mean() - fmeans[1]) > 1e-4 then 27 | print("Frame 1 doesn't seem to be correct") 28 | sys.exit(0) 29 | end 30 | frame2 = y:next_frame() 31 | print(frame2:mean()) 32 | if math.abs(frame2:mean() - fmeans[2]) > 1e-4 then 33 | print(frame2:mean(), fmeans[2]) 34 | print("Frame 2 doesn't seem to be correct") 35 | sys.exit(0) 36 | end 37 | frame3 = y:next_frame() 38 | print(frame3:mean()) 39 | if math.abs(frame3:mean() - fmeans[3]) > 1e-4 then 40 | print("Frame 3 doesn't seem to be correct") 41 | sys.exit(0) 42 | end 43 | 44 | if not y:open('v_ApplyEyeMakeup_g01_c01.avi') then 45 | print("Failed to reopen video") 46 | sys.exit(0) 47 | end 48 | 49 | frame1 = y:next_frame() 50 | if math.abs(frame1:mean() - fmeans[1]) > 1e-4 then 51 | print("Frame 1 doesn't seem to be correct") 52 | sys.exit(0) 53 | end 54 | frame2 = y:next_frame() 55 | if math.abs(frame2:mean() - fmeans[2]) > 1e-4 then 56 | print("Frame 2 doesn't seem to be correct") 57 | sys.exit(0) 58 | end 59 | frame3 = y:next_frame() 60 | if math.abs(frame3:mean() - fmeans[3]) > 1e-4 then 61 | print("Frame 3 doesn't seem to be correct") 62 | sys.exit(0) 63 | end 64 | 65 | if not y:open('/scratch/datasets/ucf101/UCF-101/ApplyEyeMakeup/v_ApplyEyeMakeup_g01_c01.avi') then 66 | print("Failed to reopen video") 67 | sys.exit(0) 68 | end 69 | 70 | assert(y:seek(0), "seek 0 failed") 71 | frame = y:next_frame() 72 | print(0, frame:mean()) 73 | assert(math.abs(frame:mean() - fmeans[1]) < 1e-4) 74 | assert(y:seek(0), "seek 0 failed") 75 | frame = y:next_frame() 76 | print(0, frame:mean()) 77 | assert(math.abs(frame:mean() - fmeans[1]) < 1e-4) 78 | print(y:length()) 79 | assert(y:seek(2), "seek 2 failed") 80 | frame = y:next_frame() 81 | print(2, frame:mean()) 82 | assert(math.abs(frame:mean() - fmeans[3]) < 1e-4) 83 | assert(y:seek(1), "seek 1 failed") 84 | frame = y:next_frame() 85 | print(1, frame:mean()) 86 | assert(math.abs(frame:mean() - fmeans[2]) < 1e-4) 87 | 88 | 89 | print("Test ok") -------------------------------------------------------------------------------- /test/ucf101_icedancing.avi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichaelMathieu/THFFmpeg/09d83170560779f53bbe7c182154fe0c4356c055/test/ucf101_icedancing.avi -------------------------------------------------------------------------------- /thffmpeg-1.0.alpha-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "thffmpeg" 2 | version = "1.0.alpha-0" 3 | 4 | source = { 5 | url = "https://github.com/MichaelMathieu/thffmpeg", 6 | tag = "master" 7 | } 8 | 9 | description = { 10 | summary = "FFMPEG bingings for Torch (reading only)", 11 | homepage = "https://github.com/MichaelMathieu/thffmpeg", 12 | license = "BSD" 13 | } 14 | 15 | dependencies = { 16 | "torch >= 7.0", 17 | "paths", 18 | "dok" 19 | } 20 | 21 | build = { 22 | type = "command", 23 | build_command = [[ 24 | cmake -E make_directory build && cd build && cmake .. -DLUALIB=$(LUALIB) -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$(LUA_BINDIR)/.." -DCMAKE_INSTALL_PREFIX="$(PREFIX)" && $(MAKE) 25 | ]], 26 | install_command = "cd build && $(MAKE) install" 27 | } -------------------------------------------------------------------------------- /thffmpeg.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016, Michael Mathieu 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | The views and conclusions contained in the software and documentation are those 26 | of the authors and should not be interpreted as representing official policies, 27 | either expressed or implied, of the FreeBSD Project. 28 | */ 29 | 30 | #include 31 | #include 32 | 33 | //#define THFFMPEG_VERBOSE 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1) 40 | #define av_frame_alloc avcodec_alloc_frame 41 | #endif 42 | 43 | #include 44 | 45 | typedef struct { 46 | AVFormatContext *pFormatCtx; 47 | AVCodecContext *pCodecCtx; 48 | AVCodec *pCodec; 49 | AVFrame *pFrame; 50 | AVFrame *pFrameRGB; 51 | AVPacket packet; 52 | uint8_t *buffer; 53 | AVDictionary *optionsDict; 54 | int videoStream; 55 | struct SwsContext *sws_ctx; 56 | int h, w; 57 | int bufferSize; 58 | } AV_Struct; 59 | 60 | static int lockmgr(void **mtx, enum AVLockOp op) { 61 | switch(op) { 62 | case AV_LOCK_CREATE: 63 | *mtx = malloc(sizeof(pthread_mutex_t)); 64 | if(!*mtx) 65 | return 1; 66 | return !!pthread_mutex_init(*mtx, NULL); 67 | case AV_LOCK_OBTAIN: 68 | return !!pthread_mutex_lock(*mtx); 69 | case AV_LOCK_RELEASE: 70 | return !!pthread_mutex_unlock(*mtx); 71 | case AV_LOCK_DESTROY: 72 | pthread_mutex_destroy(*mtx); 73 | free(*mtx); 74 | return 0; 75 | } 76 | return 1; 77 | } 78 | 79 | void AV_init() { 80 | // Register all formats and codecs 81 | if (LIBAVFORMAT_VERSION_INT != avformat_version()) { 82 | fprintf(stderr, "FFMPEG avformat header has version %d, but the library has version %d\n", 83 | LIBAVFORMAT_VERSION_INT, avformat_version()); 84 | exit(0); 85 | } 86 | if (LIBAVCODEC_VERSION_INT != avcodec_version()) { 87 | fprintf(stderr, "FFMPEG avcodec header has version %d, but the library has version %d\n", 88 | LIBAVCODEC_VERSION_INT, avcodec_version()); 89 | exit(0); 90 | } 91 | if (LIBSWSCALE_VERSION_INT != swscale_version()) { 92 | fprintf(stderr, "FFMPEG swscale header has version %d, but the library has version %d\n", 93 | LIBSWSCALE_VERSION_INT, swscale_version()); 94 | exit(0); 95 | } 96 | 97 | if (av_lockmgr_register(lockmgr)) { 98 | fprintf(stderr, "Cannot register lockmgr\n"); 99 | exit(0); 100 | } 101 | 102 | av_register_all(); 103 | 104 | #ifndef THFFMPEG_VERBOSE 105 | av_log_set_level(AV_LOG_PANIC); 106 | #endif 107 | } 108 | 109 | void AV_close(AV_Struct* avs) { 110 | uint8_t* buffer = avs->buffer; 111 | if (avs->sws_ctx != NULL) 112 | sws_freeContext(avs->sws_ctx); 113 | if (avs->pCodecCtx != NULL) 114 | avcodec_close(avs->pCodecCtx); 115 | if (avs->pFormatCtx != NULL) 116 | avformat_close_input(&(avs->pFormatCtx)); 117 | if (avs->pFrame != NULL) { 118 | av_frame_free(&avs->pFrame); //TODO: could be deallocated only once in a while (for mem leak) 119 | av_free(avs->pFrame); 120 | } 121 | if (avs->pFrameRGB != NULL) { 122 | av_frame_free(&avs->pFrameRGB); //TODO same 123 | av_free(avs->pFrameRGB); 124 | } 125 | memset(avs, 0, sizeof(AV_Struct)); 126 | avs->buffer = buffer; 127 | } 128 | 129 | int AV_open(AV_Struct* avs, const char* filename) { 130 | AV_close(avs); 131 | // Open video file 132 | if (avformat_open_input(&(avs->pFormatCtx), filename, NULL, NULL) != 0) 133 | return 0; 134 | // Retrieve stream information 135 | if (avformat_find_stream_info(avs->pFormatCtx, NULL) < 0) 136 | return 0; 137 | #ifdef THFFMPEG_VERBOSE 138 | // Dump information about file onto standard error 139 | av_dump_format(avs->pFormatCtx, 0, filename, 0); 140 | #endif 141 | // Find the first video stream 142 | avs->videoStream = -1; 143 | int i; 144 | for (i = 0; i < avs->pFormatCtx->nb_streams; ++i) { 145 | if (avs->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { 146 | avs->videoStream = i; 147 | break; 148 | } 149 | } 150 | if (avs->videoStream == -1) 151 | return 0; 152 | // Get a pointer to the codec context for the video stream 153 | avs->pCodecCtx = avs->pFormatCtx->streams[avs->videoStream]->codec; 154 | // Find the decoder for the video stream 155 | avs->pCodec = avcodec_find_decoder(avs->pCodecCtx->codec_id); 156 | if (avs->pCodec == NULL) { 157 | fprintf(stderr, "Unsupported codec!\n"); 158 | return 0; 159 | } 160 | // Open codec 161 | if (avcodec_open2(avs->pCodecCtx, avs->pCodec, &(avs->optionsDict)) < 0) 162 | return 0; 163 | // Allocate video frame 164 | avs->pFrame = av_frame_alloc(); 165 | if (avs->pFrame == NULL) 166 | return 0; 167 | // Allocate an AVFrame structure 168 | avs->pFrameRGB = av_frame_alloc(); 169 | if (avs->pFrameRGB == NULL) 170 | return 0; 171 | // Determine required buffer size and allocate buffer 172 | int numBytes = avpicture_get_size(PIX_FMT_RGB24, avs->pCodecCtx->width, 173 | avs->pCodecCtx->height); 174 | if ((avs->buffer == NULL) || (avs->bufferSize < numBytes)) { 175 | if (avs->buffer != NULL) { 176 | av_free(avs->buffer); 177 | avs->buffer = NULL; 178 | } 179 | avs->buffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t)); 180 | avs->bufferSize = numBytes; 181 | } 182 | // fill sws_ctx 183 | avs->sws_ctx = 184 | sws_getContext(avs->pCodecCtx->width, avs->pCodecCtx->height, avs->pCodecCtx->pix_fmt, 185 | avs->pCodecCtx->width, avs->pCodecCtx->height, PIX_FMT_RGB24, 186 | SWS_BILINEAR, NULL, NULL, NULL); 187 | // Assign appropriate parts of buffer to image planes in pFrameRGB 188 | // Note that pFrameRGB is an AVFrame, but AVFrame is a superset 189 | // of AVPicture 190 | avpicture_fill((AVPicture*)avs->pFrameRGB, avs->buffer, PIX_FMT_RGB24, 191 | avs->pCodecCtx->width, avs->pCodecCtx->height); 192 | avs->h = avs->pCodecCtx->height; 193 | avs->w = avs->pCodecCtx->width; 194 | return 1; 195 | } 196 | 197 | AVFrame* AV_read_frame(AV_Struct* avs) { 198 | int frameFinished; 199 | while (av_read_frame(avs->pFormatCtx, &(avs->packet)) >= 0) { 200 | // Is this a packet from the video stream? 201 | if(avs->packet.stream_index == avs->videoStream) { 202 | // Decode video frame 203 | avcodec_decode_video2(avs->pCodecCtx, avs->pFrame, &frameFinished, &avs->packet); 204 | // Did we get a video frame? 205 | if(frameFinished) { 206 | // Convert the image from its native format to RGB 207 | sws_scale(avs->sws_ctx, (uint8_t const * const *)avs->pFrame->data, 208 | avs->pFrame->linesize, 0, avs->pCodecCtx->height, 209 | avs->pFrameRGB->data, avs->pFrameRGB->linesize); 210 | //av_frame_unref(avs->pFrame); //TODO: this is opnly in ffmpeg 2.8.5 211 | // Free the packet that was allocated by av_read_frame 212 | av_free_packet(&avs->packet); 213 | return avs->pFrameRGB; 214 | } 215 | } 216 | //av_frame_unref(avs->pFrame); //TODO: this is opnly in ffmpeg 2.8.5 217 | av_free_packet(&avs->packet); 218 | } 219 | return NULL; 220 | } 221 | 222 | int AV_skip_frame(AV_Struct* avs) { 223 | int frameFinished; 224 | while (av_read_frame(avs->pFormatCtx, &(avs->packet)) >= 0) { 225 | // Is this a packet from the video stream? 226 | if(avs->packet.stream_index == avs->videoStream) { 227 | avcodec_decode_video2(avs->pCodecCtx, avs->pFrame, &frameFinished, &avs->packet); 228 | if (frameFinished) { 229 | av_free_packet(&avs->packet); 230 | return 1; 231 | } 232 | } 233 | av_free_packet(&avs->packet); 234 | } 235 | return 0; 236 | } 237 | 238 | int AV_seek(AV_Struct* avs, int64_t frame_idx) { 239 | //TODO this is probably suboptimal, but apparently that's the only precise way: 240 | // we seek to start and then skip frame_idx frames 241 | if (av_seek_frame(avs->pFormatCtx, avs->videoStream, 0, AVSEEK_FLAG_BACKWARD) < 0) 242 | return 0; 243 | avcodec_flush_buffers(avs->pCodecCtx); 244 | int i; 245 | for (i = 0; i < frame_idx; ++i) 246 | if (!AV_skip_frame(avs)) 247 | return 0; 248 | return 1; 249 | } 250 | 251 | int AV_Struct_gc(lua_State* L) { 252 | AV_Struct* avs = (AV_Struct*)lua_touserdata(L, 1); 253 | if (avs->buffer != NULL) 254 | av_free(avs->buffer); 255 | AV_close(avs); 256 | av_lockmgr_register(NULL); 257 | return 0; 258 | } 259 | 260 | int thffmpeg_Main_avinit(lua_State* L) { 261 | if (!lua_toboolean(L, 1)) 262 | AV_init(); 263 | AV_Struct* avs = (AV_Struct*)lua_newuserdata(L, sizeof(AV_Struct)); 264 | lua_newtable(L); 265 | lua_pushstring(L, "__gc"); 266 | lua_pushcfunction(L, AV_Struct_gc); 267 | lua_settable(L, -3); 268 | lua_setmetatable(L, -2); 269 | memset(avs, 0, sizeof(AV_Struct)); 270 | return 1; 271 | } 272 | 273 | int thffmpeg_Main_avopen(lua_State* L) { 274 | AV_Struct* avs = (AV_Struct*)lua_touserdata(L, 1); 275 | const char* filename = lua_tostring(L, 2); 276 | if (AV_open(avs, filename) == 0) { 277 | lua_pushnil(L); 278 | lua_pushnil(L); 279 | } else { 280 | lua_pushnumber(L, avs->h); 281 | lua_pushnumber(L, avs->w); 282 | } 283 | return 2; 284 | } 285 | 286 | int thffmpeg_Main_avclose(lua_State* L) { 287 | AV_Struct* avs = (AV_Struct*)lua_touserdata(L, 1); 288 | AV_close(avs); 289 | return 0; 290 | } 291 | 292 | int thffmpeg_Main_avseek(lua_State* L) { 293 | AV_Struct* avs = (AV_Struct*)lua_touserdata(L, 1); 294 | int64_t frame_idx = lua_tointeger(L, 2); 295 | int result = AV_seek(avs, frame_idx); 296 | lua_pushboolean(L, result); 297 | return 1; 298 | } 299 | 300 | int thffmpeg_Main_avskipframe(lua_State* L) { 301 | AV_Struct* avs = (AV_Struct*)lua_touserdata(L, 1); 302 | int result = AV_skip_frame(avs); 303 | lua_pushboolean(L, result); 304 | return 1; 305 | } 306 | 307 | int thffmpeg_Main_length(lua_State* L) { 308 | AV_Struct* avs = (AV_Struct*)lua_touserdata(L, 1); 309 | uint64_t pos = avs->pFormatCtx->pb->pos; 310 | AV_seek(avs, 0); 311 | int64_t nframes = 0; 312 | while(AV_skip_frame(avs)) 313 | ++nframes; 314 | avformat_seek_file(avs->pFormatCtx, avs->videoStream, pos, pos, pos, AVSEEK_FLAG_BYTE); 315 | avcodec_flush_buffers(avs->pCodecCtx); 316 | lua_pushinteger(L, nframes); 317 | return 1; 318 | } 319 | 320 | static const struct luaL_Reg thffmpeg_Main__ [] = { 321 | {"init", thffmpeg_Main_avinit}, 322 | {"open", thffmpeg_Main_avopen}, 323 | {"close", thffmpeg_Main_avclose}, 324 | {"seek", thffmpeg_Main_avseek}, 325 | {"skipframe", thffmpeg_Main_avskipframe}, 326 | {"length", thffmpeg_Main_length}, 327 | {NULL, NULL} 328 | }; 329 | 330 | #define torch_(NAME) TH_CONCAT_3(torch_, Real, NAME) 331 | #define torch_Tensor TH_CONCAT_STRING_3(torch., Real, Tensor) 332 | #define thffmpeg_(NAME) TH_CONCAT_3(thffmpeg_, Real, NAME) 333 | 334 | #include "generic/thffmpeg.c" 335 | #include "THGenerateAllTypes.h" 336 | 337 | DLL_EXPORT int luaopen_libthffmpeg(lua_State *L) { 338 | thffmpeg_FloatMain_init(L); 339 | thffmpeg_DoubleMain_init(L); 340 | thffmpeg_ByteMain_init(L); 341 | 342 | lua_newtable(L); 343 | lua_pushvalue(L, -1); 344 | lua_setglobal(L, "libthffmpeg"); 345 | 346 | lua_newtable(L); 347 | luaT_setfuncs(L, thffmpeg_DoubleMain__, 0); 348 | lua_setfield(L, -2, "libthffmpeg"); 349 | 350 | lua_newtable(L); 351 | luaT_setfuncs(L, thffmpeg_FloatMain__, 0); 352 | lua_setfield(L, -2, "libthffmpeg"); 353 | 354 | lua_newtable(L); 355 | luaT_setfuncs(L, thffmpeg_ByteMain__, 0); 356 | lua_setfield(L, -2, "libthffmpeg"); 357 | 358 | luaL_register(L, "libthffmpeg", thffmpeg_Main__); 359 | 360 | return 1; 361 | } 362 | --------------------------------------------------------------------------------