linux-v4l2: Support for Motion-JPEG codec
This commit is contained in:
parent
dc4de4db11
commit
1c2aea4f89
@ -29,9 +29,13 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(FFmpeg REQUIRED
|
||||
COMPONENTS avcodec avutil avformat)
|
||||
|
||||
include_directories(
|
||||
SYSTEM "${CMAKE_SOURCE_DIR}/libobs"
|
||||
${LIBV4L2_INCLUDE_DIRS}
|
||||
${FFMPEG_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(linux-v4l2_SOURCES
|
||||
@ -40,6 +44,7 @@ set(linux-v4l2_SOURCES
|
||||
v4l2-input.c
|
||||
v4l2-helpers.c
|
||||
v4l2-output.c
|
||||
v4l2-mjpeg.c
|
||||
${linux-v4l2-udev_SOURCES}
|
||||
)
|
||||
|
||||
@ -50,6 +55,7 @@ target_link_libraries(linux-v4l2
|
||||
libobs
|
||||
${LIBV4L2_LIBRARIES}
|
||||
${UDEV_LIBRARIES}
|
||||
${FFMPEG_LIBRARIES}
|
||||
)
|
||||
set_target_properties(linux-v4l2 PROPERTIES FOLDER "plugins")
|
||||
|
||||
|
@ -79,6 +79,8 @@ static inline enum video_format v4l2_to_obs_video_format(uint_fast32_t format)
|
||||
#endif
|
||||
case V4L2_PIX_FMT_BGR24:
|
||||
return VIDEO_FORMAT_BGR3;
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
return VIDEO_FORMAT_I422;
|
||||
default:
|
||||
return VIDEO_FORMAT_NONE;
|
||||
}
|
||||
|
@ -37,6 +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"
|
||||
|
||||
#if HAVE_UDEV
|
||||
#include "v4l2-udev.h"
|
||||
@ -80,6 +81,7 @@ struct v4l2_data {
|
||||
obs_source_t *source;
|
||||
pthread_t thread;
|
||||
os_event_t *event;
|
||||
struct v4l2_mjpeg_decoder mjpeg_decoder;
|
||||
|
||||
bool framerate_unchanged;
|
||||
bool resolution_unchanged;
|
||||
@ -100,6 +102,8 @@ static void v4l2_update(void *vptr, obs_data_t *settings);
|
||||
|
||||
/**
|
||||
* Prepare the output frame structure for obs and compute plane offsets
|
||||
* For encoded formats (mjpeg) this clears the frame and plane offsets,
|
||||
* which will be filled in after decoding.
|
||||
*
|
||||
* Basically all data apart from memory pointers and the timestamp is known
|
||||
* before the capture starts. This function prepares the obs_source_frame
|
||||
@ -108,6 +112,7 @@ static void v4l2_update(void *vptr, obs_data_t *settings);
|
||||
* v4l2 uses a continuous memory segment for all planes so we simply compute
|
||||
* offsets to add to the start address in order to give obs the correct data
|
||||
* pointers for the individual planes.
|
||||
*
|
||||
*/
|
||||
static void v4l2_prep_obs_frame(struct v4l2_data *data,
|
||||
struct obs_source_frame *frame,
|
||||
@ -143,6 +148,13 @@ static void v4l2_prep_obs_frame(struct v4l2_data *data,
|
||||
plane_offsets[1] = data->linesize * data->height;
|
||||
plane_offsets[2] = data->linesize * data->height * 5 / 4;
|
||||
break;
|
||||
case V4L2_PIX_FMT_MJPEG:
|
||||
frame->linesize[0] = 0;
|
||||
frame->linesize[1] = 0;
|
||||
frame->linesize[2] = 0;
|
||||
plane_offsets[1] = 0;
|
||||
plane_offsets[2] = 0;
|
||||
break;
|
||||
default:
|
||||
frame->linesize[0] = data->linesize;
|
||||
break;
|
||||
@ -257,8 +269,17 @@ static void *v4l2_thread(void *vptr)
|
||||
out.timestamp -= first_ts;
|
||||
|
||||
start = (uint8_t *)data->buffers.info[buf.index].start;
|
||||
for (uint_fast32_t i = 0; i < MAX_AV_PLANES; ++i)
|
||||
out.data[i] = start + plane_offsets[i];
|
||||
|
||||
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");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (uint_fast32_t i = 0; i < MAX_AV_PLANES; ++i)
|
||||
out.data[i] = start + plane_offsets[i];
|
||||
}
|
||||
obs_source_output_video(data->source, &out);
|
||||
|
||||
if (v4l2_ioctl(data->dev, VIDIOC_QBUF, &buf) < 0) {
|
||||
@ -890,6 +911,7 @@ static void v4l2_terminate(struct v4l2_data *data)
|
||||
data->thread = 0;
|
||||
}
|
||||
|
||||
v4l2_destroy_mjpeg(&data->mjpeg_decoder);
|
||||
v4l2_destroy_mmap(&data->buffers);
|
||||
|
||||
if (data->dev != -1) {
|
||||
@ -1003,6 +1025,11 @@ 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;
|
||||
}
|
||||
|
||||
/* start the capture thread */
|
||||
if (os_event_init(&data->event, OS_EVENT_TYPE_MANUAL) != 0)
|
||||
goto fail;
|
||||
|
98
plugins/linux-v4l2/v4l2-mjpeg.c
Normal file
98
plugins/linux-v4l2/v4l2-mjpeg.c
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright (C) 2020 by Morten Bøgeskov <source@kosmisk.dk>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <obs-module.h>
|
||||
|
||||
#include "v4l2-mjpeg.h"
|
||||
|
||||
#define blog(level, msg, ...) \
|
||||
blog(level, "v4l2-input: mjpeg: " msg, ##__VA_ARGS__)
|
||||
|
||||
int v4l2_init_mjpeg(struct v4l2_mjpeg_decoder *decoder)
|
||||
{
|
||||
decoder->codec = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
|
||||
if (!decoder->codec) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
decoder->context = avcodec_alloc_context3(decoder->codec);
|
||||
if (!decoder->context) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
decoder->packet = av_packet_alloc();
|
||||
if (!decoder->packet) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
decoder->frame = av_frame_alloc();
|
||||
if (!decoder->frame) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
decoder->context->flags2 |= AV_CODEC_FLAG2_FAST;
|
||||
decoder->context->pix_fmt = AV_PIX_FMT_YUVJ422P;
|
||||
|
||||
if (avcodec_open2(decoder->context, decoder->codec, NULL) < 0) {
|
||||
blog(LOG_ERROR, "failed to open codec");
|
||||
return -1;
|
||||
}
|
||||
|
||||
blog(LOG_DEBUG, "initialized avcodec");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void v4l2_destroy_mjpeg(struct v4l2_mjpeg_decoder *decoder)
|
||||
{
|
||||
blog(LOG_DEBUG, "destroying avcodec");
|
||||
if (decoder->frame) {
|
||||
av_frame_free(&decoder->frame);
|
||||
}
|
||||
|
||||
if (decoder->packet) {
|
||||
av_packet_free(&decoder->packet);
|
||||
}
|
||||
|
||||
if (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)
|
||||
{
|
||||
|
||||
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");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (avcodec_receive_frame(decoder->context, decoder->frame) < 0) {
|
||||
blog(LOG_ERROR, "failed to recieve frame from codec");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (uint_fast32_t i = 0; i < MAX_AV_PLANES; ++i) {
|
||||
out->data[i] = decoder->frame->data[i];
|
||||
out->linesize[i] = decoder->frame->linesize[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
68
plugins/linux-v4l2/v4l2-mjpeg.h
Normal file
68
plugins/linux-v4l2/v4l2-mjpeg.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright (C) 2020 by Morten Bøgeskov <source@kosmisk.dk>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/pixfmt.h>
|
||||
|
||||
/**
|
||||
* Data structure for mjpeg decoding
|
||||
*/
|
||||
struct v4l2_mjpeg_decoder {
|
||||
AVCodec *codec;
|
||||
AVCodecContext *context;
|
||||
AVPacket *packet;
|
||||
AVFrame *frame;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the mjpeg decoder.
|
||||
* The decoder must be destroyed on failure.
|
||||
*
|
||||
* @param props the decoder structure
|
||||
* @return non-zero on failure
|
||||
*/
|
||||
int v4l2_init_mjpeg(struct v4l2_mjpeg_decoder *decoder);
|
||||
|
||||
/**
|
||||
* Free any data associated with the decoder.
|
||||
*
|
||||
* @param decoder the decoder structure
|
||||
*/
|
||||
void v4l2_destroy_mjpeg(struct v4l2_mjpeg_decoder *decoder);
|
||||
|
||||
/**
|
||||
* Decode a jpeg into an obs frame
|
||||
*
|
||||
* @param out the obs frame to decode into
|
||||
* @param data the jpeg data
|
||||
* @param length length of the data
|
||||
* @param decoder the decoder as initialized by v4l2_init_mjpeg
|
||||
* @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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user