linux-v4l2: Add support for H.264
This commit is contained in:
parent
a1f9170180
commit
c5015d0e6c
@ -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
|
||||
|
@ -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;
|
@ -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
|
||||
}
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user