diff options
author | Julian Eisel <eiseljulian@gmail.com> | 2019-08-10 14:24:26 +0300 |
---|---|---|
committer | Julian Eisel <eiseljulian@gmail.com> | 2019-08-10 14:24:26 +0300 |
commit | b9898ebde44d6584ea37a05aaeca8760d31ccc7b (patch) | |
tree | e6dfc796cc82b4b8b8c248c4ff29d2e2047ff84c /source/blender/blenkernel/intern/writeffmpeg.c | |
parent | b17380971a144896b34b7863a82bbda3ff68a39f (diff) | |
parent | 553b581f25c1782c4231816965cd3f6ce58a449a (diff) |
Merge branch 'master' into filebrowser_redesign
Diffstat (limited to 'source/blender/blenkernel/intern/writeffmpeg.c')
-rw-r--r-- | source/blender/blenkernel/intern/writeffmpeg.c | 157 |
1 files changed, 80 insertions, 77 deletions
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index f3336adda30..5152643aaa1 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -54,6 +54,7 @@ * like M_SQRT1_2 leading to warnings with MSVC */ # include <libavformat/avformat.h> # include <libavcodec/avcodec.h> +# include <libavutil/imgutils.h> # include <libavutil/rational.h> # include <libavutil/samplefmt.h> # include <libswscale/swscale.h> @@ -80,7 +81,10 @@ typedef struct FFMpegContext { AVFormatContext *outfile; AVStream *video_stream; AVStream *audio_stream; - AVFrame *current_frame; + AVFrame *current_frame; /* Image frame in output pixel format. */ + + /* Image frame in Blender's own pixel format, may need conversion to the output pixel format. */ + AVFrame *img_convert_frame; struct SwsContext *img_convert_ctx; uint8_t *audio_input_buffer; @@ -264,6 +268,10 @@ static AVFrame *alloc_picture(int pix_fmt, int width, int height) return NULL; } avpicture_fill((AVPicture *)f, buf, pix_fmt, width, height); + f->format = pix_fmt; + f->width = width; + f->height = height; + return f; } @@ -319,6 +327,10 @@ static const char **get_file_extensions(int format) static const char *rv[] = {".ogv", ".ogg", NULL}; return rv; } + case FFMPEG_WEBM: { + static const char *rv[] = {".webm", NULL}; + return rv; + } default: return NULL; } @@ -371,67 +383,52 @@ static int write_video_frame( } /* read and encode a frame of audio from the buffer */ -static AVFrame *generate_video_frame(FFMpegContext *context, uint8_t *pixels, ReportList *reports) +static AVFrame *generate_video_frame(FFMpegContext *context, + const uint8_t *pixels, + ReportList *reports) { - uint8_t *rendered_frame; - AVCodecContext *c = context->video_stream->codec; - int width = c->width; int height = c->height; AVFrame *rgb_frame; - if (c->pix_fmt != AV_PIX_FMT_BGR32) { - rgb_frame = alloc_picture(AV_PIX_FMT_BGR32, width, height); - if (!rgb_frame) { - BKE_report(reports, RPT_ERROR, "Could not allocate temporary frame"); - return NULL; - } + if (context->img_convert_frame != NULL) { + /* Pixel format conversion is needed. */ + rgb_frame = context->img_convert_frame; } else { + /* The output pixel format is Blender's internal pixel format. */ rgb_frame = context->current_frame; } - rendered_frame = pixels; + /* Copy the Blender pixels into the FFmpeg datastructure, taking care of endianness and flipping + * the image vertically. */ + int linesize = rgb_frame->linesize[0]; + for (int y = 0; y < height; y++) { + uint8_t *target = rgb_frame->data[0] + linesize * (height - y - 1); + const uint8_t *src = pixels + linesize * y; - /* Do RGBA-conversion and flipping in one step depending - * on CPU-Endianess */ +# if ENDIAN_ORDER == L_ENDIAN + memcpy(target, src, linesize); - if (ENDIAN_ORDER == L_ENDIAN) { - int y; - for (y = 0; y < height; y++) { - uint8_t *target = rgb_frame->data[0] + width * 4 * (height - y - 1); - uint8_t *src = rendered_frame + width * 4 * y; - uint8_t *end = src + width * 4; - while (src != end) { - target[3] = src[3]; - target[2] = src[2]; - target[1] = src[1]; - target[0] = src[0]; +# elif ENDIAN_ORDER == B_ENDIAN + const uint8_t *end = src + linesize; + while (src != end) { + target[3] = src[0]; + target[2] = src[1]; + target[1] = src[2]; + target[0] = src[3]; - target += 4; - src += 4; - } - } - } - else { - int y; - for (y = 0; y < height; y++) { - uint8_t *target = rgb_frame->data[0] + width * 4 * (height - y - 1); - uint8_t *src = rendered_frame + width * 4 * y; - uint8_t *end = src + width * 4; - while (src != end) { - target[3] = src[0]; - target[2] = src[1]; - target[1] = src[2]; - target[0] = src[3]; - - target += 4; - src += 4; - } + target += 4; + src += 4; } +# else +# error ENDIAN_ORDER should either be L_ENDIAN or B_ENDIAN. +# endif } - if (c->pix_fmt != AV_PIX_FMT_BGR32) { + /* Convert to the output pixel format, if it's different that Blender's internal one. */ + if (context->img_convert_frame != NULL) { + BLI_assert(context->img_convert_ctx != NULL); sws_scale(context->img_convert_ctx, (const uint8_t *const *)rgb_frame->data, rgb_frame->linesize, @@ -439,13 +436,8 @@ static AVFrame *generate_video_frame(FFMpegContext *context, uint8_t *pixels, Re c->height, context->current_frame->data, context->current_frame->linesize); - delete_picture(rgb_frame); } - context->current_frame->format = AV_PIX_FMT_BGR32; - context->current_frame->width = width; - context->current_frame->height = height; - return context->current_frame; } @@ -696,9 +688,15 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } if (codec_id == AV_CODEC_ID_QTRLE) { - /* Always write to ARGB. The default pixel format of QTRLE is RGB24, which uses 3 bytes per - * pixels, which breaks the export. */ - c->pix_fmt = AV_PIX_FMT_ARGB; + if (rd->im_format.planes == R_IMF_PLANES_RGBA) { + c->pix_fmt = AV_PIX_FMT_ARGB; + } + } + + if (codec_id == AV_CODEC_ID_VP9) { + if (rd->im_format.planes == R_IMF_PLANES_RGBA) { + c->pix_fmt = AV_PIX_FMT_YUVA420P; + } } if (codec_id == AV_CODEC_ID_PNG) { @@ -727,18 +725,29 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } av_dict_free(&opts); + /* FFmpeg expects its data in the output pixel format. */ context->current_frame = alloc_picture(c->pix_fmt, c->width, c->height); - context->img_convert_ctx = sws_getContext(c->width, - c->height, - AV_PIX_FMT_BGR32, - c->width, - c->height, - c->pix_fmt, - SWS_BICUBIC, - NULL, - NULL, - NULL); + if (c->pix_fmt == AV_PIX_FMT_RGBA) { + /* Output pixel format is the same we use internally, no conversion necessary. */ + context->img_convert_frame = NULL; + context->img_convert_ctx = NULL; + } + else { + /* Output pixel format is different, allocate frame for conversion. */ + context->img_convert_frame = alloc_picture(AV_PIX_FMT_RGBA, c->width, c->height); + context->img_convert_ctx = sws_getContext(c->width, + c->height, + AV_PIX_FMT_RGBA, + c->width, + c->height, + c->pix_fmt, + SWS_BICUBIC, + NULL, + NULL, + NULL); + } + return st; } @@ -1427,6 +1436,11 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit) delete_picture(context->current_frame); context->current_frame = NULL; } + if (context->img_convert_frame != NULL) { + delete_picture(context->img_convert_frame); + context->img_convert_frame = NULL; + } + if (context->outfile != NULL && context->outfile->oformat) { if (!(context->outfile->oformat->flags & AVFMT_NOFILE)) { avio_close(context->outfile->pb); @@ -1836,25 +1850,14 @@ bool BKE_ffmpeg_alpha_channel_is_supported(RenderData *rd) { int codec = rd->ffcodecdata.codec; - if (codec == AV_CODEC_ID_QTRLE) { - return true; - } - - if (codec == AV_CODEC_ID_PNG) { - return true; - } - - if (codec == AV_CODEC_ID_HUFFYUV) { - return true; - } - # ifdef FFMPEG_FFV1_ALPHA_SUPPORTED + /* Visual Studio 2019 doesn't like #ifdef within ELEM(). */ if (codec == AV_CODEC_ID_FFV1) { return true; } # endif - return false; + return ELEM(codec, AV_CODEC_ID_QTRLE, AV_CODEC_ID_PNG, AV_CODEC_ID_VP9, AV_CODEC_ID_HUFFYUV); } void *BKE_ffmpeg_context_create(void) |