|  |  |  | #include "FrameReader.hpp"
 | 
					
						
							|  |  |  | #include <assert.h>
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ffmpeg_lockmgr_cb(void **arg, enum AVLockOp op) {
 | 
					
						
							|  |  |  |   pthread_mutex_t *mutex = (pthread_mutex_t *)*arg;
 | 
					
						
							|  |  |  |   int err;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   switch (op) {
 | 
					
						
							|  |  |  |   case AV_LOCK_CREATE:
 | 
					
						
							|  |  |  |     mutex = (pthread_mutex_t *)malloc(sizeof(*mutex));
 | 
					
						
							|  |  |  |     if (!mutex)
 | 
					
						
							|  |  |  |         return AVERROR(ENOMEM);
 | 
					
						
							|  |  |  |     if ((err = pthread_mutex_init(mutex, NULL))) {
 | 
					
						
							|  |  |  |         free(mutex);
 | 
					
						
							|  |  |  |         return AVERROR(err);
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |     *arg = mutex;
 | 
					
						
							|  |  |  |     return 0;
 | 
					
						
							|  |  |  |   case AV_LOCK_OBTAIN:
 | 
					
						
							|  |  |  |     if ((err = pthread_mutex_lock(mutex)))
 | 
					
						
							|  |  |  |         return AVERROR(err);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0;
 | 
					
						
							|  |  |  |   case AV_LOCK_RELEASE:
 | 
					
						
							|  |  |  |     if ((err = pthread_mutex_unlock(mutex)))
 | 
					
						
							|  |  |  |         return AVERROR(err);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0;
 | 
					
						
							|  |  |  |   case AV_LOCK_DESTROY:
 | 
					
						
							|  |  |  |     if (mutex)
 | 
					
						
							|  |  |  |         pthread_mutex_destroy(mutex);
 | 
					
						
							|  |  |  |     free(mutex);
 | 
					
						
							|  |  |  |     *arg = NULL;
 | 
					
						
							|  |  |  |     return 0;
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   return 1;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | FrameReader::FrameReader(const char *fn) {
 | 
					
						
							|  |  |  |   int ret;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ret = av_lockmgr_register(ffmpeg_lockmgr_cb);
 | 
					
						
							|  |  |  |   assert(ret >= 0);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   avformat_network_init();
 | 
					
						
							|  |  |  |   av_register_all();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   snprintf(url, sizeof(url)-1,"%s",fn);
 | 
					
						
							|  |  |  |   t = new std::thread([&]() { this->loaderThread(); });
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FrameReader::loaderThread() {
 | 
					
						
							|  |  |  |   int ret;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0) {
 | 
					
						
							|  |  |  |     fprintf(stderr, "error loading %s\n", url);
 | 
					
						
							|  |  |  |     valid = false;
 | 
					
						
							|  |  |  |     return;
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   av_dump_format(pFormatCtx, 0, url, 0);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   auto pCodecCtxOrig = pFormatCtx->streams[0]->codec;
 | 
					
						
							|  |  |  |   auto pCodec = avcodec_find_decoder(pCodecCtxOrig->codec_id);
 | 
					
						
							|  |  |  |   assert(pCodec != NULL);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   pCodecCtx = avcodec_alloc_context3(pCodec);
 | 
					
						
							|  |  |  |   ret = avcodec_copy_context(pCodecCtx, pCodecCtxOrig);
 | 
					
						
							|  |  |  |   assert(ret == 0);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   ret = avcodec_open2(pCodecCtx, pCodec, NULL);
 | 
					
						
							|  |  |  |   assert(ret >= 0);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sws_ctx = sws_getContext(width, height, AV_PIX_FMT_YUV420P,
 | 
					
						
							|  |  |  |                            width, height, AV_PIX_FMT_BGR24,
 | 
					
						
							|  |  |  |                            SWS_BILINEAR, NULL, NULL, NULL);
 | 
					
						
							|  |  |  |   assert(sws_ctx != NULL);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   AVPacket *pkt = (AVPacket *)malloc(sizeof(AVPacket));
 | 
					
						
							|  |  |  |   assert(pkt != NULL);
 | 
					
						
							|  |  |  |   bool first = true;
 | 
					
						
							|  |  |  |   while (av_read_frame(pFormatCtx, pkt)>=0) {
 | 
					
						
							|  |  |  |     //printf("%d pkt %d %d\n", pkts.size(), pkt->size, pkt->pos);
 | 
					
						
							|  |  |  |     if (first) {
 | 
					
						
							|  |  |  |       AVFrame *pFrame = av_frame_alloc();
 | 
					
						
							|  |  |  |       int frameFinished;
 | 
					
						
							|  |  |  |       avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, pkt);
 | 
					
						
							|  |  |  |       first = false;
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |     pkts.push_back(pkt);
 | 
					
						
							|  |  |  |     pkt = (AVPacket *)malloc(sizeof(AVPacket));
 | 
					
						
							|  |  |  |     assert(pkt != NULL);
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   free(pkt);
 | 
					
						
							|  |  |  |   printf("framereader download done\n");
 | 
					
						
							|  |  |  |   joined = true;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // cache
 | 
					
						
							|  |  |  |   while (1) {
 | 
					
						
							|  |  |  |     GOPCache(to_cache.get());
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FrameReader::GOPCache(int idx) {
 | 
					
						
							|  |  |  |   AVFrame *pFrame;
 | 
					
						
							|  |  |  |   int gop = idx - idx%15;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   mcache.lock();
 | 
					
						
							|  |  |  |   bool has_gop = cache.find(gop) != cache.end();
 | 
					
						
							|  |  |  |   mcache.unlock();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!has_gop) {
 | 
					
						
							|  |  |  |     //printf("caching %d\n", gop);
 | 
					
						
							|  |  |  |     for (int i = gop; i < gop+15; i++) {
 | 
					
						
							|  |  |  |       if (i >= pkts.size()) break;
 | 
					
						
							|  |  |  |       //printf("decode %d\n", i);
 | 
					
						
							|  |  |  |       int frameFinished;
 | 
					
						
							|  |  |  |       pFrame = av_frame_alloc();
 | 
					
						
							|  |  |  |       avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, pkts[i]);
 | 
					
						
							|  |  |  |       uint8_t *dat = toRGB(pFrame)->data[0];
 | 
					
						
							|  |  |  |       mcache.lock();
 | 
					
						
							|  |  |  |       cache.insert(std::make_pair(i, dat));
 | 
					
						
							|  |  |  |       mcache.unlock();
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AVFrame *FrameReader::toRGB(AVFrame *pFrame) {
 | 
					
						
							|  |  |  |   AVFrame *pFrameRGB = av_frame_alloc();
 | 
					
						
							|  |  |  |   int numBytes = avpicture_get_size(AV_PIX_FMT_BGR24, pFrame->width, pFrame->height);
 | 
					
						
							|  |  |  |   uint8_t *buffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
 | 
					
						
							|  |  |  |   avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_BGR24, pFrame->width, pFrame->height);
 | 
					
						
							|  |  |  | 	sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
 | 
					
						
							|  |  |  | 						pFrame->linesize, 0, pFrame->height,
 | 
					
						
							|  |  |  | 						pFrameRGB->data, pFrameRGB->linesize);
 | 
					
						
							|  |  |  |   return pFrameRGB;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uint8_t *FrameReader::get(int idx) {
 | 
					
						
							|  |  |  |   if (!valid) return NULL;
 | 
					
						
							|  |  |  |   waitForReady();
 | 
					
						
							|  |  |  |   // TODO: one line?
 | 
					
						
							|  |  |  |   uint8_t *dat = NULL;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // lookahead
 | 
					
						
							|  |  |  |   to_cache.put(idx);
 | 
					
						
							|  |  |  |   to_cache.put(idx+15);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   mcache.lock();
 | 
					
						
							|  |  |  |   auto it = cache.find(idx);
 | 
					
						
							|  |  |  |   if (it != cache.end()) {
 | 
					
						
							|  |  |  |     dat = it->second;
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   mcache.unlock();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (dat == NULL) {
 | 
					
						
							|  |  |  |     to_cache.put_front(idx);
 | 
					
						
							|  |  |  |     // lookahead
 | 
					
						
							|  |  |  |     while (dat == NULL) {
 | 
					
						
							|  |  |  |       // wait for frame
 | 
					
						
							|  |  |  |       usleep(50*1000);
 | 
					
						
							|  |  |  |       // check for frame
 | 
					
						
							|  |  |  |       mcache.lock();
 | 
					
						
							|  |  |  |       auto it = cache.find(idx);
 | 
					
						
							|  |  |  |       if (it != cache.end()) dat = it->second;
 | 
					
						
							|  |  |  |       mcache.unlock();
 | 
					
						
							|  |  |  |       if (dat == NULL) {
 | 
					
						
							|  |  |  |         printf(".");
 | 
					
						
							|  |  |  |         fflush(stdout);
 | 
					
						
							|  |  |  |       }
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  |   }
 | 
					
						
							|  |  |  |   return dat;
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 |