From 1d4f5d708de0d8ec19300b417354a3374d00ed47 Mon Sep 17 00:00:00 2001
From: Good Guy <good1.2guy@gmail.com>
Date: Mon, 6 May 2019 17:08:59 -0600
Subject: [PATCH] add ffmpeg vaapi hw encode, colorpicker fix alpha, titler
 layout timecode, booby titler outline

---
 cinelerra-5.1/cinelerra/colorpicker.C       |   2 +-
 cinelerra-5.1/cinelerra/ffmpeg.C            | 115 ++++++++++++++++++--
 cinelerra-5.1/cinelerra/ffmpeg.h            |   3 +
 cinelerra-5.1/ffmpeg/video/h264_vaapi.mp4   |   3 +
 cinelerra-5.1/ffmpeg/video/hevc_vaapi.mp4   |   2 +
 cinelerra-5.1/ffmpeg/video/mjpeg_vaapi.mp4  |   2 +
 cinelerra-5.1/ffmpeg/video/mpeg2_vaapi.mp4  |   2 +
 cinelerra-5.1/plugins/titler/titlerwindow.C |  20 +++-
 cinelerra-5.1/plugins/titler/titlerwindow.h |   3 +-
 9 files changed, 137 insertions(+), 15 deletions(-)
 create mode 100644 cinelerra-5.1/ffmpeg/video/h264_vaapi.mp4
 create mode 100644 cinelerra-5.1/ffmpeg/video/hevc_vaapi.mp4
 create mode 100644 cinelerra-5.1/ffmpeg/video/mjpeg_vaapi.mp4
 create mode 100644 cinelerra-5.1/ffmpeg/video/mpeg2_vaapi.mp4

diff --git a/cinelerra-5.1/cinelerra/colorpicker.C b/cinelerra-5.1/cinelerra/colorpicker.C
index 7238d59d..927807b6 100644
--- a/cinelerra-5.1/cinelerra/colorpicker.C
+++ b/cinelerra-5.1/cinelerra/colorpicker.C
@@ -1405,7 +1405,7 @@ void ColorCircleButton::create_objects()
 void ColorCircleButton::set_color(int color)
 {
 	this->color = (color & 0xffffff);
-	this->alpha = (~color>>24);
+	this->alpha = (~color>>24) & 0xff;
 	int r = (color>>16) & 0xff;
 	int g = (color>>8) & 0xff;
 	int b = (color>>0) & 0xff;
diff --git a/cinelerra-5.1/cinelerra/ffmpeg.C b/cinelerra-5.1/cinelerra/ffmpeg.C
index 9f13ec0d..33941868 100644
--- a/cinelerra-5.1/cinelerra/ffmpeg.C
+++ b/cinelerra-5.1/cinelerra/ffmpeg.C
@@ -99,6 +99,12 @@ void FFrame::dequeue()
 	fst->dequeue(this);
 }
 
+void FFrame::set_hw_frame(AVFrame *frame)
+{
+	av_frame_free(&frm);
+	frm = frame;
+}
+
 int FFAudioStream::read(float *fp, long len)
 {
 	long n = len * nch;
@@ -383,10 +389,12 @@ int FFStream::decode_activate()
 				ret = avcodec_open2(avctx, decoder, &copts);
 			}
 			if( ret >= 0 && hw_type != AV_HWDEVICE_TYPE_NONE ) {
-				ret = read_packet();
+				if( need_packet ) {
+					need_packet = 0;
+					ret = read_packet();
+				}
 				if( ret >= 0 ) {
 					AVPacket *pkt = (AVPacket*)ipkt;
-					need_packet = 0;
 					ret = avcodec_send_packet(avctx, pkt);
 					if( ret < 0 || hw_pix_fmt == AV_PIX_FMT_NONE ) {
 						ff_err(ret, "HW device init failed, using SW decode.\nfile:%s\n",
@@ -396,9 +404,11 @@ int FFStream::decode_activate()
 						av_buffer_unref(&hw_device_ctx);
 						hw_device_ctx = 0;
 						hw_type = AV_HWDEVICE_TYPE_NONE;
-						flushed = 0;
-						st_eof(0);
-						need_packet = 1;
+						int flags = AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY;
+						int idx = st->index;
+						av_seek_frame(fmt_ctx, idx, INT64_MIN, flags);
+						need_packet = 1;  flushed = 0;
+						seeked = 1;  st_eof(0);
 						ret = 0;
 						continue;
 					}
@@ -1045,6 +1055,75 @@ void FFVideoStream::decode_hw_format(AVCodec *decoder, AVHWDeviceType type)
 	}
 }
 
+AVHWDeviceType FFVideoStream::encode_hw_activate(const char *hw_dev)
+{
+	AVBufferRef *hw_device_ctx = 0;
+	AVBufferRef *hw_frames_ref = 0;
+	AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
+	if( strcmp(_("none"), hw_dev) ) {
+		type = av_hwdevice_find_type_by_name(hw_dev);
+		if( type != AV_HWDEVICE_TYPE_VAAPI ) {
+			fprintf(stderr, "currently, only vaapi hw encode is supported\n");
+			type = AV_HWDEVICE_TYPE_NONE;
+		}
+	}
+	if( type != AV_HWDEVICE_TYPE_NONE ) {
+		int ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, 0, 0, 0);
+		if( ret < 0 ) {
+			ff_err(ret, "Failed to create a HW device.\n");
+			type = AV_HWDEVICE_TYPE_NONE;
+		}
+	}
+	if( type != AV_HWDEVICE_TYPE_NONE ) {
+		hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx);
+		if( !hw_frames_ref ) {
+			fprintf(stderr, "Failed to create HW frame context.\n");
+			type = AV_HWDEVICE_TYPE_NONE;
+		}
+	}
+	if( type != AV_HWDEVICE_TYPE_NONE ) {
+		AVHWFramesContext *frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data);
+		frames_ctx->format = AV_PIX_FMT_VAAPI;
+		frames_ctx->sw_format = AV_PIX_FMT_NV12;
+		frames_ctx->width = width;
+		frames_ctx->height = height;
+		frames_ctx->initial_pool_size = 0; // 200;
+		int ret = av_hwframe_ctx_init(hw_frames_ref);
+		if( ret >= 0 ) {
+			avctx->hw_frames_ctx = av_buffer_ref(hw_frames_ref);
+			if( !avctx->hw_frames_ctx ) ret = AVERROR(ENOMEM);
+		}
+		if( ret < 0 ) {
+			ff_err(ret, "Failed to initialize HW frame context.\n");
+			type = AV_HWDEVICE_TYPE_NONE;
+		}
+		av_buffer_unref(&hw_frames_ref);
+	}
+	return type;
+}
+
+int FFVideoStream::encode_hw_write(FFrame *picture)
+{
+	int ret = 0;
+	AVFrame *hw_frm = 0;
+	switch( avctx->pix_fmt ) {
+	case AV_PIX_FMT_VAAPI:
+		hw_frm = av_frame_alloc();
+		if( !hw_frm ) { ret = AVERROR(ENOMEM);  break; }
+		ret = av_hwframe_get_buffer(avctx->hw_frames_ctx, hw_frm, 0);
+		if( ret < 0 ) break;
+		ret = av_hwframe_transfer_data(hw_frm, *picture, 0);
+		if( ret < 0 ) break;
+		picture->set_hw_frame(hw_frm);
+		return 0;
+	default:
+		return 0;
+	}
+	av_frame_free(&hw_frm);
+	ff_err(ret, "Error while transferring frame data to GPU.\n");
+	return ret;
+}
+
 int FFVideoStream::decode_frame(AVFrame *frame)
 {
 	int first_frame = seeked;  seeked = 0;
@@ -1103,7 +1182,14 @@ int FFVideoStream::video_seek(int64_t pos)
 
 int FFVideoStream::init_frame(AVFrame *picture)
 {
-	picture->format = avctx->pix_fmt;
+	switch( avctx->pix_fmt ) {
+	case AV_PIX_FMT_VAAPI:
+		picture->format = AV_PIX_FMT_NV12;
+		break;
+	default:
+		picture->format = avctx->pix_fmt;
+		break;
+	}
 	picture->width  = avctx->width;
 	picture->height = avctx->height;
 	int ret = av_frame_get_buffer(picture, 32);
@@ -1121,6 +1207,8 @@ int FFVideoStream::encode(VFrame *vframe)
 		frame->pts = curr_pos;
 		ret = convert_pixfmt(vframe, frame);
 	}
+	if( ret >= 0 && avctx->hw_frames_ctx )
+		encode_hw_write(picture);
 	if( ret >= 0 ) {
 		picture->queue(curr_pos);
 		++curr_pos;
@@ -2393,10 +2481,23 @@ int FFMPEG::open_encoder(const char *type, const char *spec)
 			vid->height = asset->height;
 			vid->frame_rate = asset->frame_rate;
 
-			AVPixelFormat pix_fmt = av_get_pix_fmt(asset->ff_pixel_format);
+			AVPixelFormat pix_fmt = AV_PIX_FMT_NONE;
+			if( opt_hw_dev != 0 ) {
+				AVHWDeviceType hw_type = vid->encode_hw_activate(opt_hw_dev);
+				switch( hw_type ) {
+				case AV_HWDEVICE_TYPE_VAAPI:
+					pix_fmt = AV_PIX_FMT_VAAPI;
+					break;
+				case AV_HWDEVICE_TYPE_NONE:
+				default:
+					pix_fmt = av_get_pix_fmt(asset->ff_pixel_format);
+					break;
+				}
+			}
 			if( pix_fmt == AV_PIX_FMT_NONE )
 				pix_fmt = codec->pix_fmts ? codec->pix_fmts[0] : AV_PIX_FMT_YUV420P;
 			ctx->pix_fmt = pix_fmt;
+
 			const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
 			int mask_w = (1<<desc->log2_chroma_w)-1;
 			ctx->width = (vid->width+mask_w) & ~mask_w;
diff --git a/cinelerra-5.1/cinelerra/ffmpeg.h b/cinelerra-5.1/cinelerra/ffmpeg.h
index 15659944..9bfd268a 100644
--- a/cinelerra-5.1/cinelerra/ffmpeg.h
+++ b/cinelerra-5.1/cinelerra/ffmpeg.h
@@ -68,6 +68,7 @@ public:
 	int initted() { return init; }
 	void queue(int64_t pos);
 	void dequeue();
+	void set_hw_frame(AVFrame *frame);
 };
 
 class FFStream {
@@ -235,6 +236,8 @@ public:
 	int decode_frame(AVFrame *frame);
 	AVHWDeviceType decode_hw_activate();
 	void decode_hw_format(AVCodec *decoder, AVHWDeviceType type);
+	AVHWDeviceType encode_hw_activate(const char *hw_dev);
+	int encode_hw_write(FFrame *picture);
 	int encode_frame(AVFrame *frame);
 	int create_filter(const char *filter_spec, AVCodecParameters *avpar);
 	void load_markers();
diff --git a/cinelerra-5.1/ffmpeg/video/h264_vaapi.mp4 b/cinelerra-5.1/ffmpeg/video/h264_vaapi.mp4
new file mode 100644
index 00000000..085d70d4
--- /dev/null
+++ b/cinelerra-5.1/ffmpeg/video/h264_vaapi.mp4
@@ -0,0 +1,3 @@
+mp4 h264_vaapi
+cin_hw_dev=vaapi
+profile=high
diff --git a/cinelerra-5.1/ffmpeg/video/hevc_vaapi.mp4 b/cinelerra-5.1/ffmpeg/video/hevc_vaapi.mp4
new file mode 100644
index 00000000..4e57f2b4
--- /dev/null
+++ b/cinelerra-5.1/ffmpeg/video/hevc_vaapi.mp4
@@ -0,0 +1,2 @@
+mp4 hevc_vaapi
+cin_hw_dev=vaapi
diff --git a/cinelerra-5.1/ffmpeg/video/mjpeg_vaapi.mp4 b/cinelerra-5.1/ffmpeg/video/mjpeg_vaapi.mp4
new file mode 100644
index 00000000..14a00d80
--- /dev/null
+++ b/cinelerra-5.1/ffmpeg/video/mjpeg_vaapi.mp4
@@ -0,0 +1,2 @@
+mp4 mjpeg_vaapi
+cin_hw_dev=vaapi
diff --git a/cinelerra-5.1/ffmpeg/video/mpeg2_vaapi.mp4 b/cinelerra-5.1/ffmpeg/video/mpeg2_vaapi.mp4
new file mode 100644
index 00000000..f13cbb00
--- /dev/null
+++ b/cinelerra-5.1/ffmpeg/video/mpeg2_vaapi.mp4
@@ -0,0 +1,2 @@
+mp4 mpeg2_vaapi
+cin_hw_dev=vaapi
diff --git a/cinelerra-5.1/plugins/titler/titlerwindow.C b/cinelerra-5.1/plugins/titler/titlerwindow.C
index 251389cd..0cf4c8fe 100644
--- a/cinelerra-5.1/plugins/titler/titlerwindow.C
+++ b/cinelerra-5.1/plugins/titler/titlerwindow.C
@@ -367,10 +367,16 @@ void TitleWindow::create_objects()
 	stroker->create_objects();
 	x += stroker->get_w() + margin;
 #endif
-	y += outline_title->get_h() + margin;
-	add_tool(timecode = new TitleTimecode(client, this, x1=x, y));
-	x += timecode->get_w() + margin;
-	add_tool(timecode_format = new TitleTimecodeFormat(client, this, x, y,
+	add_tool(timecode = new TitleTimecode(client, this, x, y));
+	y += timecode->get_h() + margin;
+	int tw = 0;
+	for( int i=0; i<lengthof(timeunit_formats); ++i ) {
+		char text[BCSTRLEN];
+		Units::print_time_format(timeunit_formats[i], text);
+		int w = get_text_width(MEDIUMFONT, text);
+		if( tw < w ) tw = w;
+	}
+	add_tool(timecode_format = new TitleTimecodeFormat(client, this, x, y, tw,
 		Units::print_time_format(client->config.timecode_format, string)));
 	timecode_format->create_objects();
 	y += timecode_format->get_h() + margin;
@@ -806,7 +812,9 @@ void TitleOutlineColorButton::handle_done_event(int result)
 {
 	if( result ) {
 		handle_new_color(orig_color, orig_alpha);
+		window->lock_window("TitleColorButton::handle_done_event");
 		update_gui(orig_color);
+		window->unlock_window();
 	}
 }
 
@@ -852,8 +860,8 @@ int TitleTimecode::handle_event()
 }
 
 TitleTimecodeFormat::TitleTimecodeFormat(TitleMain *client, TitleWindow *window,
-		int x, int y, const char *text)
- : BC_PopupMenu(x, y, 100, text, 1)
+		int x, int y, int tw, const char *text)
+ : BC_PopupMenu(x, y, BC_PopupMenu::calculate_w(tw)+10, text, 1)
 {
 	this->client = client;
 	this->window = window;
diff --git a/cinelerra-5.1/plugins/titler/titlerwindow.h b/cinelerra-5.1/plugins/titler/titlerwindow.h
index 10ca4724..43145ebf 100644
--- a/cinelerra-5.1/plugins/titler/titlerwindow.h
+++ b/cinelerra-5.1/plugins/titler/titlerwindow.h
@@ -332,7 +332,8 @@ public:
 class TitleTimecodeFormat : public BC_PopupMenu
 {
 public:
-	TitleTimecodeFormat(TitleMain *client, TitleWindow *window, int x, int y, const char *text);
+	TitleTimecodeFormat(TitleMain *client, TitleWindow *window,
+		int x, int y, int tw, const char *text);
 	void create_objects();
 	int update(int timecode_format);
 	int handle_event();
-- 
2.26.2