Vuo  2.3.2
VuoFfmpegUtility.h
Go to the documentation of this file.
1 
10 #pragma once
11 
12 #ifdef __cplusplus
13 extern "C"
14 {
15 #include "module.h"
16 #include "VuoImage.h"
17 #include "VuoAudioSamples.h"
18 
19 #pragma clang diagnostic push
20 #pragma clang diagnostic ignored "-Wdocumentation"
21 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
22 #include <libavcodec/avcodec.h>
23 #include <libavformat/avformat.h>
24 #include <libavutil/avutil.h>
25 #include <libavutil/imgutils.h>
26 #include <libswscale/swscale.h>
27 #include <string.h>
28 #include <libswresample/swresample.h>
29 #include <libavutil/opt.h>
30 #pragma clang diagnostic pop
31 #endif
32 
33 #pragma clang diagnostic push
34 #pragma clang diagnostic ignored "-Wunused-function"
35 
40 {
42  const double USEC_TO_SECOND = .000001;
43 
45  const double SEC_TO_USEC = 1000000;
46 
48  static double AvTimeToSecond(AVStream* stream, int64_t pts)
49  {
50  if (pts == INT64_MAX)
51  return DBL_MAX;
52  return av_rescale_q(pts, stream->time_base, AV_TIME_BASE_Q) * USEC_TO_SECOND;
53  }
54 
56  static int64_t AvTimeToMicrosecond(AVStream* stream, int64_t pts)
57  {
58  if( pts == AV_NOPTS_VALUE )
59  VUserLog("Timestamp value is invalid!");
60  return av_rescale_q(pts, stream->time_base, AV_TIME_BASE_Q);
61  }
62 
64  static int64_t SecondToAvTime(AVStream* stream, double second)
65  {
66  // convert second to millisecond
67  int64_t ms = av_rescale(second * 1000, stream->time_base.den, stream->time_base.num);
68 
69  // convert to microsecond
70  return ms / 1000;
71  }
72 
76  static int FirstStreamIndexWithMediaType(AVFormatContext* context, AVMediaType type)
77  {
78  for(int i = 0; i < context->nb_streams; i++)
79  if( context->streams[i]->codecpar->codec_type == type)
80  return i;
81  return -1;
82  }
83 
87  static void FlipImageBytesVertical(uint8_t* buffer, uint width, uint height)
88  {
89  uint row_size_bytes = width * sizeof(uint8_t);
90  uint8_t* tmp = (uint8_t*)malloc(row_size_bytes);
91 
92  for(uint i = 0; i < height/2; i++)
93  {
94  memcpy(tmp, buffer + width * i, row_size_bytes);
95  memcpy(buffer + width * i, buffer + width * (height-i-1), row_size_bytes);
96  memcpy(buffer + width * (height-i-1), tmp, row_size_bytes);
97  }
98  free(tmp);
99  }
100 
102  bool warnedAboutSlowFlip = false;
103 
107  static bool ConvertAVFrameToRGB(AVFrame *dst, AVPixelFormat dst_pix_fmt, AVFrame *src, AVPixelFormat pix_fmt, int width, int height)
108  {
109  SwsContext *img_convert_ctx = sws_getContext(width, height, pix_fmt, width, height, dst_pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);
110 
111  bool slowFlip = false;
112  int planeCount = av_pix_fmt_count_planes((AVPixelFormat)src->format);
113 
114  // Flip vertically without copying by starting on the last row and using a negative row stride.
115  if (planeCount == 1
116  || src->format == AV_PIX_FMT_YUV422P
117  || src->format == AV_PIX_FMT_YUVJ422P
118  || src->format == AV_PIX_FMT_YUV422P10LE
119  || src->format == AV_PIX_FMT_YUVJ444P
120  || src->format == AV_PIX_FMT_YUVA444P12LE
121  || src->format == AV_PIX_FMT_YUV411P
122  || src->format == AV_PIX_FMT_YUVJ411P)
123  {
124  // All planes have the same height.
125  for (int i = 0; i < planeCount; ++i)
126  {
127  src->data[i] += src->linesize[i] * (src->height - 1);
128  src->linesize[i] = -src->linesize[i];
129  }
130  }
131  else if (src->format == AV_PIX_FMT_YUV420P
132  || src->format == AV_PIX_FMT_YUVJ420P)
133  {
134  // 3 planes, last 2 half height.
135  for (int i = 0; i < 3; ++i)
136  {
137  if (i == 0)
138  src->data[i] += src->linesize[i] * (src->height - 1);
139  else
140  src->data[i] += src->linesize[i] * (src->height/2 - 1);
141  src->linesize[i] = -src->linesize[i];
142  }
143  }
144  else
145  {
146  if (!warnedAboutSlowFlip)
147  {
148  VUserLog("Unknown pixelformat %s (%d planes); using slow flipper.", av_get_pix_fmt_name((AVPixelFormat)src->format), planeCount);
149  warnedAboutSlowFlip = true;
150  }
151  slowFlip = true;
152  }
153 
154  int result = sws_scale(img_convert_ctx, src->data, src->linesize, 0, height, dst->data, dst->linesize);
155  sws_freeContext(img_convert_ctx);
156 
157  if (slowFlip)
158  FlipImageBytesVertical(dst->data[0], dst->linesize[0], height);
159 
160  return result;
161  }
162 
166  static VuoImage VuoImageWithAVFrame(AVCodecContext* videoCodecCtx, AVFrame* frame)
167  {
168  VuoImage image = NULL;
169  AVPixelFormat pixelFormat = AV_PIX_FMT_RGBA;
170 
171  // Allocate an AVFrame structure
172  AVFrame *pFrameRGB = av_frame_alloc();
173  if (!pFrameRGB)
174  return image;
175 
176  int linesize = av_image_get_linesize(pixelFormat, videoCodecCtx->width, 0);
177  int numBytes = av_image_get_buffer_size(pixelFormat, videoCodecCtx->width, videoCodecCtx->height, linesize);
178  uint8_t *buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
179 
180  av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, pixelFormat, videoCodecCtx->width, videoCodecCtx->height, 1);
181 
182  // Convert the image from its native format to RGB
183  ConvertAVFrameToRGB(pFrameRGB, pixelFormat, frame, videoCodecCtx->pix_fmt, videoCodecCtx->width, videoCodecCtx->height);
184 
185  // Write frame to GL texture and create a VuoImage for it, then break and return.
186  image = VuoImage_makeFromBuffer(pFrameRGB->data[0], GL_RGBA, videoCodecCtx->width, videoCodecCtx->height, VuoImageColorDepth_8, ^(void *){
187  av_free(buffer);
188  av_free(pFrameRGB);
189  });
190 
191  return image;
192  }
193 }
194 
195 #pragma clang diagnostic pop
196 
197 #ifdef __cplusplus
198 }
199 #endif