graphics-ffmpeg.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #include "graphics.h"
  2. #include <libavcodec/avcodec.h>
  3. #include <libavformat/avformat.h>
  4. #include <libswscale/swscale.h>
  5. #include "../obs-ffmpeg-compat.h"
  6. struct ffmpeg_image {
  7. const char *file;
  8. AVFormatContext *fmt_ctx;
  9. AVCodecContext *decoder_ctx;
  10. AVCodec *decoder;
  11. AVStream *stream;
  12. int stream_idx;
  13. int cx, cy;
  14. enum AVPixelFormat format;
  15. };
  16. static bool ffmpeg_image_open_decoder_context(struct ffmpeg_image *info)
  17. {
  18. int ret = av_find_best_stream(info->fmt_ctx, AVMEDIA_TYPE_VIDEO,
  19. -1, 1, NULL, 0);
  20. if (ret < 0) {
  21. blog(LOG_WARNING, "Couldn't find video stream in file '%s': %s",
  22. info->file, av_err2str(ret));
  23. return false;
  24. }
  25. info->stream_idx = ret;
  26. info->stream = info->fmt_ctx->streams[ret];
  27. info->decoder_ctx = info->stream->codec;
  28. info->decoder = avcodec_find_decoder(info->decoder_ctx->codec_id);
  29. if (!info->decoder) {
  30. blog(LOG_WARNING, "Failed to find decoder for file '%s'",
  31. info->file);
  32. return false;
  33. }
  34. ret = avcodec_open2(info->decoder_ctx, info->decoder, NULL);
  35. if (ret < 0) {
  36. blog(LOG_WARNING, "Failed to open video codec for file '%s': "
  37. "%s", info->file, av_err2str(ret));
  38. return false;
  39. }
  40. return true;
  41. }
  42. static void ffmpeg_image_free(struct ffmpeg_image *info)
  43. {
  44. avcodec_close(info->decoder_ctx);
  45. avformat_close_input(&info->fmt_ctx);
  46. }
  47. static bool ffmpeg_image_init(struct ffmpeg_image *info, const char *file)
  48. {
  49. int ret;
  50. memset(info, 0, sizeof(struct ffmpeg_image));
  51. info->file = file;
  52. info->stream_idx = -1;
  53. ret = avformat_open_input(&info->fmt_ctx, file, NULL, NULL);
  54. if (ret < 0) {
  55. blog(LOG_WARNING, "Failed to open file '%s': %s",
  56. info->file, av_err2str(ret));
  57. return false;
  58. }
  59. ret = avformat_find_stream_info(info->fmt_ctx, NULL);
  60. if (ret < 0) {
  61. blog(LOG_WARNING, "Could not find stream info for file '%s':"
  62. " %s", info->file, av_err2str(ret));
  63. goto fail;
  64. }
  65. if (!ffmpeg_image_open_decoder_context(info))
  66. goto fail;
  67. info->cx = info->decoder_ctx->width;
  68. info->cy = info->decoder_ctx->height;
  69. info->format = info->decoder_ctx->pix_fmt;
  70. return true;
  71. fail:
  72. ffmpeg_image_free(info);
  73. return false;
  74. }
  75. static bool ffmpeg_image_reformat_frame(struct ffmpeg_image *info,
  76. AVFrame *frame, uint8_t *out, int linesize)
  77. {
  78. struct SwsContext *sws_ctx = NULL;
  79. int ret = 0;
  80. if (info->format == AV_PIX_FMT_RGBA ||
  81. info->format == AV_PIX_FMT_BGRA ||
  82. info->format == AV_PIX_FMT_BGR0) {
  83. if (linesize != frame->linesize[0]) {
  84. int min_line = linesize < frame->linesize[0] ?
  85. linesize : frame->linesize[0];
  86. for (int y = 0; y < info->cy; y++)
  87. memcpy(out + y * linesize,
  88. frame->data[0] + y * frame->linesize[0],
  89. min_line);
  90. } else {
  91. memcpy(out, frame->data[0], linesize * info->cy);
  92. }
  93. } else {
  94. sws_ctx = sws_getContext(info->cx, info->cy, info->format,
  95. info->cx, info->cy, AV_PIX_FMT_BGRA,
  96. SWS_POINT, NULL, NULL, NULL);
  97. if (!sws_ctx) {
  98. blog(LOG_WARNING, "Failed to create scale context "
  99. "for '%s'", info->file);
  100. return false;
  101. }
  102. ret = sws_scale(sws_ctx, (const uint8_t *const*)frame->data,
  103. frame->linesize, 0, info->cy, &out, &linesize);
  104. sws_freeContext(sws_ctx);
  105. if (ret < 0) {
  106. blog(LOG_WARNING, "sws_scale failed for '%s': %s",
  107. info->file, av_err2str(ret));
  108. return false;
  109. }
  110. info->format = AV_PIX_FMT_BGRA;
  111. }
  112. return true;
  113. }
  114. static bool ffmpeg_image_decode(struct ffmpeg_image *info, uint8_t *out,
  115. int linesize)
  116. {
  117. AVPacket packet = {0};
  118. bool success = false;
  119. AVFrame *frame = av_frame_alloc();
  120. int got_frame = 0;
  121. int ret;
  122. if (!frame) {
  123. blog(LOG_WARNING, "Failed to create frame data for '%s'",
  124. info->file);
  125. return false;
  126. }
  127. ret = av_read_frame(info->fmt_ctx, &packet);
  128. if (ret < 0) {
  129. blog(LOG_WARNING, "Failed to read image frame from '%s': %s",
  130. info->file, av_err2str(ret));
  131. goto fail;
  132. }
  133. while (!got_frame) {
  134. ret = avcodec_decode_video2(info->decoder_ctx, frame,
  135. &got_frame, &packet);
  136. if (ret < 0) {
  137. blog(LOG_WARNING, "Failed to decode frame for '%s': %s",
  138. info->file, av_err2str(ret));
  139. goto fail;
  140. }
  141. }
  142. success = ffmpeg_image_reformat_frame(info, frame, out, linesize);
  143. fail:
  144. av_free_packet(&packet);
  145. av_frame_free(&frame);
  146. return success;
  147. }
  148. void gs_init_image_deps(void)
  149. {
  150. av_register_all();
  151. }
  152. void gs_free_image_deps(void)
  153. {
  154. }
  155. static inline enum gs_color_format convert_format(enum AVPixelFormat format)
  156. {
  157. switch ((int)format) {
  158. case AV_PIX_FMT_RGBA: return GS_RGBA;
  159. case AV_PIX_FMT_BGRA: return GS_BGRA;
  160. case AV_PIX_FMT_BGR0: return GS_BGRX;
  161. }
  162. return GS_BGRX;
  163. }
  164. texture_t gs_create_texture_from_file(const char *file)
  165. {
  166. struct ffmpeg_image image;
  167. texture_t tex = NULL;
  168. if (ffmpeg_image_init(&image, file)) {
  169. uint8_t *data = malloc(image.cx * image.cy * 4);
  170. if (ffmpeg_image_decode(&image, data, image.cx * 4)) {
  171. tex = gs_create_texture(image.cx, image.cy,
  172. convert_format(image.format),
  173. 1, (const uint8_t**)&data, 0);
  174. }
  175. ffmpeg_image_free(&image);
  176. free(data);
  177. }
  178. return tex;
  179. }