VideoExtractor.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. // VideoExtractor.cpp
  2. #include "VideoExtractor.h"
  3. extern "C"
  4. {
  5. #include <libavcodec/avcodec.h>
  6. #include <libavformat/avformat.h>
  7. #include <libswscale/swscale.h>
  8. }
  9. #include <stdio.h>
  10. #include <fstream>
  11. #include <sys/stat.h>
  12. #include <cstdlib>
  13. using namespace std;
  14. #define MAX_FRAME_WIDTH 1920
  15. #define MAX_CHARS_PER_LINE 512
  16. #define TOKENS_PER_LINE 2
  17. // compatibility with newer API
  18. #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
  19. #define av_frame_alloc avcodec_alloc_frame
  20. #define av_frame_free avcodec_free_frame
  21. #endif
  22. // constants for saving frames and video data
  23. const string basePath = "/tmp/gta-vi/";
  24. const string frameBasename = "frame";
  25. const string frameExt = ".ppm";
  26. const string paramsFile = "video_parameters.txt";
  27. // PUBLIC
  28. /*static*/ bool VideoExtractor::IsVideoExtracted(string videoname)
  29. {
  30. VideoParams videoParams;
  31. string paramsname = GetParamsname();
  32. if (!LoadVideoParams(paramsname, videoParams))
  33. return false;
  34. if (videoParams.videoname.compare(videoname) != 0)
  35. return false;
  36. return true;
  37. }
  38. /*static*/ bool VideoExtractor::ExtractFrames(string videoname)
  39. {
  40. // Initalizing these to NULL prevents segfaults!
  41. AVFormatContext *pFormatCtx = NULL;
  42. size_t i;
  43. int videoStream;
  44. AVCodecContext *pCodecCtxOrig = NULL;
  45. AVCodecContext *pCodecCtx = NULL;
  46. AVCodec *pCodec = NULL;
  47. AVFrame *pFrame = NULL;
  48. AVFrame *pFrameRGB = NULL;
  49. AVPacket packet;
  50. int frameFinished;
  51. int numBytes;
  52. uint8_t *buffer = NULL;
  53. struct SwsContext *sws_ctx = NULL;
  54. VideoParams videoParams; // parameters of the stored video
  55. videoParams.videoname = videoname;
  56. // Register all formats and codecs
  57. av_register_all();
  58. // Open video file
  59. if(avformat_open_input(&pFormatCtx, videoname.c_str(), NULL, NULL)!=0)
  60. return false; // Couldn't open file
  61. // Retrieve stream information
  62. if(avformat_find_stream_info(pFormatCtx, NULL)<0)
  63. return false; // Couldn't find stream information
  64. // Dump information about file onto standard error
  65. av_dump_format(pFormatCtx, 0, videoname.c_str(), 0);
  66. // Find the first video stream
  67. videoStream=-1;
  68. for(i=0; i<pFormatCtx->nb_streams; i++)
  69. if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
  70. videoStream=i;
  71. break;
  72. }
  73. if(videoStream==-1)
  74. return false; // Didn't find a video stream
  75. // Get a pointer to the codec context for the video stream
  76. pCodecCtxOrig=pFormatCtx->streams[videoStream]->codec;
  77. // Get frame rate
  78. videoParams.fps_num = pFormatCtx->streams[videoStream]->avg_frame_rate.num;
  79. videoParams.fps_den = pFormatCtx->streams[videoStream]->avg_frame_rate.den;
  80. // Find the decoder for the video stream
  81. pCodec=avcodec_find_decoder(pCodecCtxOrig->codec_id);
  82. if(pCodec==NULL) {
  83. fprintf(stderr, "Unsupported codec!\n");
  84. return false; // Codec not found
  85. }
  86. // Copy context
  87. pCodecCtx = avcodec_alloc_context3(pCodec);
  88. if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) {
  89. fprintf(stderr, "Couldn't copy codec context");
  90. return false; // Error copying codec context
  91. }
  92. // Open codec
  93. if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
  94. return false; // Could not open codec
  95. // Allocate video frame
  96. pFrame=av_frame_alloc();
  97. // Allocate an AVFrame structure
  98. pFrameRGB=av_frame_alloc();
  99. if(pFrameRGB==NULL)
  100. return false;
  101. // Determine required buffer size and allocate buffer
  102. numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
  103. pCodecCtx->height);
  104. buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
  105. int out_width = pCodecCtx->width;
  106. int out_height = pCodecCtx->height;
  107. while (out_width > MAX_FRAME_WIDTH)
  108. {
  109. out_width /= 2; // halving size makes extraction faster
  110. out_height = (int)((double)out_width*pCodecCtx->height/pCodecCtx->width);
  111. }
  112. videoParams.width = out_width;
  113. videoParams.height = out_height;
  114. //int out_height = 300;
  115. // Assign appropriate parts of buffer to image planes in pFrameRGB
  116. // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
  117. // of AVPicture
  118. avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
  119. out_width, out_height);
  120. // initialize SWS context for software scaling
  121. sws_ctx = sws_getContext(pCodecCtx->width,
  122. pCodecCtx->height,
  123. pCodecCtx->pix_fmt,
  124. out_width,
  125. out_height,
  126. PIX_FMT_RGB24,
  127. SWS_BILINEAR,
  128. NULL,
  129. NULL,
  130. NULL
  131. );
  132. // make sure the output directory exists
  133. if (!CreateBaseDir())
  134. return false;
  135. // Read frames and save first five frames to disk
  136. i=0;
  137. while(av_read_frame(pFormatCtx, &packet)>=0) {
  138. // Is this a packet from the video stream?
  139. if(packet.stream_index==videoStream) {
  140. // Decode video frame
  141. avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
  142. // Did we get a video frame?
  143. if(frameFinished) {
  144. // Convert the image from its native format to RGB
  145. sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
  146. pFrame->linesize, 0, pCodecCtx->height,
  147. pFrameRGB->data, pFrameRGB->linesize);
  148. // Save the frame to disk
  149. SaveFrame(pFrameRGB, out_width, out_height,i++);
  150. }
  151. }
  152. // Free the packet that was allocated by av_read_frame
  153. av_free_packet(&packet);
  154. }
  155. videoParams.numOfFrames = i;
  156. string paramsFile = GetParamsname();
  157. SaveVideoParams(videoParams, paramsFile);
  158. // Free the RGB image
  159. av_free(buffer);
  160. av_frame_free(&pFrameRGB);
  161. // Free the YUV frame
  162. av_frame_free(&pFrame);
  163. // Close the codecs
  164. avcodec_close(pCodecCtx);
  165. avcodec_close(pCodecCtxOrig);
  166. // Close the video file
  167. avformat_close_input(&pFormatCtx);
  168. return true;
  169. }
  170. // PRIVATE
  171. /*static*/ string VideoExtractor::GetFramename(int frameNum)
  172. {
  173. string frameName = basePath + frameBasename + to_string(frameNum) + frameExt;
  174. return frameName;
  175. }
  176. /*static*/ string VideoExtractor::GetParamsname()
  177. {
  178. return basePath+paramsFile;
  179. }
  180. /*static*/ void VideoExtractor::SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
  181. FILE *pFile;
  182. int y;
  183. // Open file
  184. //sprintf(szFilename, "frame%d.ppm", iFrame);
  185. //pFile=fopen(szFilename, "wb");
  186. string framename = GetFramename(iFrame);
  187. pFile=fopen(framename.c_str(), "wb");
  188. if(pFile==NULL)
  189. return;
  190. // Write header
  191. fprintf(pFile, "P6\n%d %d\n255\n", width, height);
  192. // Write pixel data
  193. for(y=0; y<height; y++)
  194. fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
  195. // Close file
  196. fclose(pFile);
  197. }
  198. // functions for storing and loading video parameters
  199. /*static*/ void VideoExtractor::SaveVideoParams(VideoParams videoParams, string filename)
  200. {
  201. ofstream ofile;
  202. ofile.open(filename.c_str());
  203. ofile << "videoname: " << videoParams.videoname << "\n";
  204. ofile << "fps_num: " << videoParams.fps_num << "\n";
  205. ofile << "fps_den: " << videoParams.fps_den << "\n";
  206. ofile << "width: " << videoParams.width << "\n";
  207. ofile << "height: " << videoParams.height << "\n";
  208. ofile << "numOfFrames: " << videoParams.numOfFrames << "\n";
  209. ofile.close();
  210. }
  211. /*static*/ bool VideoExtractor::LoadVideoParams(string filename, VideoParams &rVideoParams)
  212. {
  213. ifstream ifile;
  214. ifile.open(filename.c_str());
  215. if (!ifile.is_open())
  216. return false;
  217. char buf[MAX_CHARS_PER_LINE];
  218. char *token[TOKENS_PER_LINE];
  219. while(!ifile.getline(buf, MAX_CHARS_PER_LINE).eof())
  220. {
  221. token[0] = strtok(buf, " ");
  222. token[1] = strtok(0, " ");
  223. if (string(token[0]).compare("videoname:") == 0)
  224. rVideoParams.videoname = token[1];
  225. else if (string(token[0]).compare("fps_num:") == 0)
  226. rVideoParams.fps_num = atoi(token[1]);
  227. else if (string(token[0]).compare("fps_den:") == 0)
  228. rVideoParams.fps_den = atoi(token[1]);
  229. else if (string(token[0]).compare("width:") == 0)
  230. rVideoParams.width = atoi(token[1]);
  231. else if (string(token[0]).compare("height:") == 0)
  232. rVideoParams.height = atoi(token[1]);
  233. else if (string(token[0]).compare("numOfFrames:") == 0)
  234. rVideoParams.numOfFrames = atoi(token[1]);
  235. }
  236. ifile.close();
  237. return true;
  238. }
  239. /*static*/ bool VideoExtractor::CreateBaseDir()
  240. {
  241. struct stat info;
  242. // directory already exists
  243. if( stat(basePath.c_str(), &info) == 0 )
  244. return true;
  245. // otherwise create it
  246. string command = "mkdir -p " + basePath;
  247. //const int dir_err = mkdir(basePath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); // this command cannot create last level directory if all the precious directories do not exist
  248. const int dir_err = system(command.c_str());
  249. if (dir_err == 0)
  250. return true;
  251. return false;
  252. }