Browse Source

linux-v4l2: Add support for H.264

Grzegorz Godlewski 3 years ago
parent
commit
c5015d0e6c

+ 1 - 1
plugins/linux-v4l2/CMakeLists.txt

@@ -15,7 +15,7 @@ add_library(linux-v4l2 MODULE)
 add_library(OBS::v4l2 ALIAS linux-v4l2)
 
 target_sources(linux-v4l2 PRIVATE linux-v4l2.c v4l2-controls.c v4l2-input.c
-                                  v4l2-helpers.c v4l2-output.c v4l2-mjpeg.c)
+                                  v4l2-helpers.c v4l2-output.c v4l2-decoder.c)
 
 target_link_libraries(
   linux-v4l2 PRIVATE OBS::libobs LIB4L2::LIB4L2 FFmpeg::avcodec

+ 20 - 11
plugins/linux-v4l2/v4l2-mjpeg.c → plugins/linux-v4l2/v4l2-decoder.c

@@ -16,16 +16,26 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include <obs-module.h>
+#include <linux/videodev2.h>
 
-#include "v4l2-mjpeg.h"
+#include "v4l2-decoder.h"
 
 #define blog(level, msg, ...) \
-	blog(level, "v4l2-input: mjpeg: " msg, ##__VA_ARGS__)
+	blog(level, "v4l2-input: decoder: " msg, ##__VA_ARGS__)
 
-int v4l2_init_mjpeg(struct v4l2_mjpeg_decoder *decoder)
+int v4l2_init_decoder(struct v4l2_decoder *decoder, int pixfmt)
 {
-	decoder->codec = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
+	if (pixfmt == V4L2_PIX_FMT_MJPEG) {
+		decoder->codec = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
+	} else if (pixfmt == V4L2_PIX_FMT_H264) {
+		decoder->codec = avcodec_find_decoder(AV_CODEC_ID_H264);
+	}
 	if (!decoder->codec) {
+		if (pixfmt == V4L2_PIX_FMT_MJPEG) {
+			blog(LOG_ERROR, "failed to find MJPEG decoder");
+		} else if (pixfmt == V4L2_PIX_FMT_H264) {
+			blog(LOG_ERROR, "failed to find H264 decoder");
+		}
 		return -1;
 	}
 
@@ -56,7 +66,7 @@ int v4l2_init_mjpeg(struct v4l2_mjpeg_decoder *decoder)
 	return 0;
 }
 
-void v4l2_destroy_mjpeg(struct v4l2_mjpeg_decoder *decoder)
+void v4l2_destroy_decoder(struct v4l2_decoder *decoder)
 {
 	blog(LOG_DEBUG, "destroying avcodec");
 	if (decoder->frame) {
@@ -68,22 +78,23 @@ void v4l2_destroy_mjpeg(struct v4l2_mjpeg_decoder *decoder)
 	}
 
 	if (decoder->context) {
+		avcodec_close(decoder->context);
 		avcodec_free_context(&decoder->context);
 	}
 }
 
-int v4l2_decode_mjpeg(struct obs_source_frame *out, uint8_t *data,
-		      size_t length, struct v4l2_mjpeg_decoder *decoder)
+int v4l2_decode_frame(struct obs_source_frame *out, uint8_t *data,
+		      size_t length, struct v4l2_decoder *decoder)
 {
 	decoder->packet->data = data;
 	decoder->packet->size = length;
 	if (avcodec_send_packet(decoder->context, decoder->packet) < 0) {
-		blog(LOG_ERROR, "failed to send jpeg to codec");
+		blog(LOG_ERROR, "failed to send frame to codec");
 		return -1;
 	}
 
 	if (avcodec_receive_frame(decoder->context, decoder->frame) < 0) {
-		blog(LOG_ERROR, "failed to recieve frame from codec");
+		blog(LOG_ERROR, "failed to receive frame from codec");
 		return -1;
 	}
 
@@ -105,8 +116,6 @@ int v4l2_decode_mjpeg(struct obs_source_frame *out, uint8_t *data,
 	case AV_PIX_FMT_YUV444P:
 		out->format = VIDEO_FORMAT_I444;
 		break;
-	default:
-		break;
 	}
 
 	return 0;

+ 13 - 12
plugins/linux-v4l2/v4l2-mjpeg.h → plugins/linux-v4l2/v4l2-decoder.h

@@ -26,42 +26,43 @@ extern "C" {
 #include <libavutil/pixfmt.h>
 
 /**
- * Data structure for mjpeg decoding
+ * Data structure for decoder
  */
-struct v4l2_mjpeg_decoder {
-	const AVCodec *codec;
+struct v4l2_decoder {
+	AVCodec *codec;
 	AVCodecContext *context;
 	AVPacket *packet;
 	AVFrame *frame;
 };
 
 /**
- * Initialize the mjpeg decoder.
+ * Initialize the decoder.
  * The decoder must be destroyed on failure.
  *
- * @param props the decoder structure
+ * @param decoder the decoder structure
+ * @param pixfmt which codec is used
  * @return non-zero on failure
  */
-int v4l2_init_mjpeg(struct v4l2_mjpeg_decoder *decoder);
+int v4l2_init_decoder(struct v4l2_decoder *decoder, int pixfmt);
 
 /**
  * Free any data associated with the decoder.
  *
  * @param decoder the decoder structure
  */
-void v4l2_destroy_mjpeg(struct v4l2_mjpeg_decoder *decoder);
+void v4l2_destroy_decoder(struct v4l2_decoder *decoder);
 
 /**
- * Decode a jpeg into an obs frame
+ * Decode a jpeg or h264 frame into an obs frame
  *
  * @param out the obs frame to decode into
- * @param data the jpeg data
+ * @param data the codec data
  * @param length length of the data
- * @param decoder the decoder as initialized by v4l2_init_mjpeg
+ * @param decoder the decoder as initialized by v4l2_init_decoder
  * @return non-zero on failure
  */
-int v4l2_decode_mjpeg(struct obs_source_frame *out, uint8_t *data,
-		      size_t length, struct v4l2_mjpeg_decoder *decoder);
+int v4l2_decode_frame(struct obs_source_frame *out, uint8_t *data,
+		      size_t length, struct v4l2_decoder *decoder);
 
 #ifdef __cplusplus
 }

+ 22 - 12
plugins/linux-v4l2/v4l2-input.c

@@ -37,7 +37,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #include "v4l2-controls.h"
 #include "v4l2-helpers.h"
-#include "v4l2-mjpeg.h"
+#include "v4l2-decoder.h"
 
 #if HAVE_UDEV
 #include "v4l2-udev.h"
@@ -81,7 +81,7 @@ struct v4l2_data {
 	obs_source_t *source;
 	pthread_t thread;
 	os_event_t *event;
-	struct v4l2_mjpeg_decoder mjpeg_decoder;
+	struct v4l2_decoder decoder;
 
 	bool framerate_unchanged;
 	bool resolution_unchanged;
@@ -268,10 +268,12 @@ static void *v4l2_thread(void *vptr)
 
 		start = (uint8_t *)data->buffers.info[buf.index].start;
 
-		if (data->pixfmt == V4L2_PIX_FMT_MJPEG) {
-			if (v4l2_decode_mjpeg(&out, start, buf.bytesused,
-					      &data->mjpeg_decoder) < 0) {
-				blog(LOG_ERROR, "failed to unpack jpeg");
+		if (data->pixfmt == V4L2_PIX_FMT_MJPEG ||
+		    data->pixfmt == V4L2_PIX_FMT_H264) {
+			if (v4l2_decode_frame(&out, start, buf.bytesused,
+					      &data->decoder) < 0) {
+				blog(LOG_ERROR,
+				     "failed to unpack jpeg or h264");
 				break;
 			}
 		} else {
@@ -477,7 +479,8 @@ static void v4l2_format_list(int dev, obs_property_t *prop)
 
 		if (v4l2_to_obs_video_format(fmt.pixelformat) !=
 			    VIDEO_FORMAT_NONE ||
-		    fmt.pixelformat == V4L2_PIX_FMT_MJPEG) {
+		    fmt.pixelformat == V4L2_PIX_FMT_MJPEG ||
+		    fmt.pixelformat == V4L2_PIX_FMT_H264) {
 			obs_property_list_add_int(prop, buffer.array,
 						  fmt.pixelformat);
 			blog(LOG_INFO, "Pixelformat: %s (available)",
@@ -910,7 +913,10 @@ static void v4l2_terminate(struct v4l2_data *data)
 		data->thread = 0;
 	}
 
-	v4l2_destroy_mjpeg(&data->mjpeg_decoder);
+	if (data->pixfmt == V4L2_PIX_FMT_MJPEG ||
+	    data->pixfmt == V4L2_PIX_FMT_H264) {
+		v4l2_destroy_decoder(&data->decoder);
+	}
 	v4l2_destroy_mmap(&data->buffers);
 
 	if (data->dev != -1) {
@@ -1002,7 +1008,8 @@ static void v4l2_init(struct v4l2_data *data)
 		goto fail;
 	}
 	if (v4l2_to_obs_video_format(data->pixfmt) == VIDEO_FORMAT_NONE &&
-	    data->pixfmt != V4L2_PIX_FMT_MJPEG) {
+	    data->pixfmt != V4L2_PIX_FMT_MJPEG &&
+	    data->pixfmt != V4L2_PIX_FMT_H264) {
 		blog(LOG_ERROR, "Selected video format not supported");
 		goto fail;
 	}
@@ -1025,9 +1032,12 @@ static void v4l2_init(struct v4l2_data *data)
 		goto fail;
 	}
 
-	if (v4l2_init_mjpeg(&data->mjpeg_decoder) < 0) {
-		blog(LOG_ERROR, "Failed to initialize mjpeg decoder");
-		goto fail;
+	if (data->pixfmt == V4L2_PIX_FMT_MJPEG ||
+	    data->pixfmt == V4L2_PIX_FMT_H264) {
+		if (v4l2_init_decoder(&data->decoder, data->pixfmt) < 0) {
+			blog(LOG_ERROR, "Failed to initialize decoder");
+			goto fail;
+		}
 	}
 
 	/* start the capture thread */