Browse Source

Add NV12 conversion shader

Palana 11 years ago
parent
commit
4edadcc0a6
2 changed files with 105 additions and 0 deletions
  1. 73 0
      build/data/libobs/format_conversion.effect
  2. 32 0
      libobs/obs.c

+ 73 - 0
build/data/libobs/format_conversion.effect

@@ -56,6 +56,70 @@ VertInOut VSDefault(VertInOut vert_in)
 /* used to prevent internal GPU precision issues width fmod in particular */
 #define PRECISION_OFFSET 0.1
 
+float4 PSNV12(VertInOut vert_in) : TARGET
+{
+#ifdef _OPENGL
+	float v_mul = floor((1.0 - vert_in.uv.y) * input_height);
+#else
+	float v_mul = floor(vert_in.uv.y * input_height);
+#endif
+
+	float byte_offset = floor((v_mul + vert_in.uv.x) * width) * 4.0;
+	byte_offset += PRECISION_OFFSET;
+
+	float2 sample_pos[4];
+
+	if (byte_offset < u_plane_offset) {
+#ifdef DEBUGGING
+		return float4(1.0, 1.0, 1.0, 1.0);
+#endif
+
+		float lum_u = floor(fmod(byte_offset, width)) * width_i;
+		float lum_v = floor(byte_offset * width_i)    * height_i;
+
+		/* move to texel centers to sample the 4 pixels properly */
+		lum_u += width_i  * 0.5;
+		lum_v += height_i * 0.5;
+
+		sample_pos[0] = float2(lum_u,            lum_v);
+		sample_pos[1] = float2(lum_u += width_i, lum_v);
+		sample_pos[2] = float2(lum_u += width_i, lum_v);
+		sample_pos[3] = float2(lum_u +  width_i, lum_v);
+
+		float4x4 out_val = float4x4(
+			image.Sample(def_sampler, sample_pos[0]),
+			image.Sample(def_sampler, sample_pos[1]),
+			image.Sample(def_sampler, sample_pos[2]),
+			image.Sample(def_sampler, sample_pos[3])
+		);
+
+		return transpose(out_val)[1];
+	} else {
+#ifdef DEBUGGING
+		return float4(0.5, 0.2, 0.5, 0.2);
+#endif
+
+		float new_offset = byte_offset - u_plane_offset;
+
+		float ch_u = floor(fmod(new_offset, width)) * width_i;
+		float ch_v = floor(new_offset * width_i)    * height_d2_i;
+		float width_i2 = width_i*2.0;
+
+		/* move to the borders of each set of 4 pixels to force it
+		 * to do bilinear averaging */
+		ch_u += width_i;
+		ch_v += height_i;
+
+		sample_pos[0] = float2(ch_u,             ch_v);
+		sample_pos[1] = float2(ch_u + width_i2,  ch_v);
+		
+		return float4(
+				image.Sample(def_sampler, sample_pos[0]).rb,
+				image.Sample(def_sampler, sample_pos[1]).rb
+				);
+	}
+}
+
 float4 PSPlanar420(VertInOut vert_in) : TARGET
 {
 #ifdef _OPENGL
@@ -137,3 +201,12 @@ technique Planar420
 		pixel_shader  = PSPlanar420(vert_in);
 	}
 }
+
+technique NV12
+{
+	pass
+	{
+		vertex_shader = VSDefault(vert_in);
+		pixel_shader  = PSNV12(vert_in);
+	}
+}

+ 32 - 0
libobs/obs.c

@@ -83,6 +83,34 @@ static inline void set_420p_sizes(const struct obs_video_info *ovi)
 	video->conversion_tech = "Planar420";
 }
 
+static inline void set_nv12_sizes(const struct obs_video_info *ovi)
+{
+	struct obs_core_video *video = &obs->video;
+	uint32_t chroma_pixels;
+	uint32_t total_bytes;
+
+	chroma_pixels = (ovi->output_width * ovi->output_height / 2);
+	chroma_pixels = GET_ALIGN(chroma_pixels, PIXEL_SIZE);
+
+	video->plane_offsets[0] = 0;
+	video->plane_offsets[1] = ovi->output_width * ovi->output_height;
+
+	video->plane_linewidth[0] = ovi->output_width;
+	video->plane_linewidth[1] = ovi->output_width;
+
+	video->plane_sizes[0] = video->plane_offsets[1];
+	video->plane_sizes[1] = video->plane_sizes[0]/2;
+
+	total_bytes = video->plane_offsets[1] + chroma_pixels;
+
+	video->conversion_height =
+		(total_bytes/PIXEL_SIZE + ovi->output_width-1) /
+		ovi->output_width;
+
+	video->conversion_height = GET_ALIGN(video->conversion_height, 2);
+	video->conversion_tech = "NV12";
+}
+
 static inline void calc_gpu_conversion_sizes(const struct obs_video_info *ovi)
 {
 	obs->video.conversion_height = 0;
@@ -94,6 +122,10 @@ static inline void calc_gpu_conversion_sizes(const struct obs_video_info *ovi)
 	switch ((uint32_t)ovi->output_format) {
 	case VIDEO_FORMAT_I420:
 		set_420p_sizes(ovi);
+		break;
+	case VIDEO_FORMAT_NV12:
+		set_nv12_sizes(ovi);
+		break;
 	}
 }