Skip to content

Commit

Permalink
Write jpegs using avcodec
Browse files Browse the repository at this point in the history
  • Loading branch information
webgeek1234 committed Jul 5, 2024
1 parent 474abe6 commit ddcc149
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 10 deletions.
60 changes: 52 additions & 8 deletions src/zm_event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,46 @@ Event::Event(
id = zmDbDoInsert(sql);
} while (!id and !zm_terminate);

const AVCodec* mJpegCodec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
if (!mJpegCodec) {
Error("MJPEG codec not found");
return;
}

mJpegCodecContext = avcodec_alloc_context3(mJpegCodec);
if (!mJpegCodecContext) {
Error("Could not allocate jpeg codec context");
return;
}

mJpegCodecContext->bit_rate = 400000;
mJpegCodecContext->width = monitor->Width();
mJpegCodecContext->height = monitor->Height();
mJpegCodecContext->time_base= (AVRational) {1,25};
mJpegCodecContext->pix_fmt = AV_PIX_FMT_YUVJ420P;

if (avcodec_open2(mJpegCodecContext, mJpegCodec, NULL) < 0) {
Error("Could not open mjpeg codec");
return;
}

AVPixelFormat format;
switch (monitor->Colours()) {
case ZM_COLOUR_RGB24:
format = (monitor->SubpixelOrder() == ZM_SUBPIX_ORDER_BGR ? AV_PIX_FMT_BGR24 : AV_PIX_FMT_RGB24);
break;
case ZM_COLOUR_GRAY8:
format = AV_PIX_FMT_GRAY8;
break;
default:
format = AV_PIX_FMT_RGBA;
break;
};
mJpegSwsContext = sws_getContext(
mJpegCodecContext->width, mJpegCodecContext->height, format,
mJpegCodecContext->width, mJpegCodecContext->height, AV_PIX_FMT_YUV420P,
SWS_BICUBIC, nullptr, nullptr, nullptr);

thread_ = std::thread(&Event::Run, this);
}

Expand Down Expand Up @@ -220,6 +260,16 @@ Event::~Event() {
id);
zmDbDoUpdate(sql);
} // end if no changed rows due to Name change during recording

if (mJpegCodecContext) {
avcodec_close(mJpegCodecContext);
avcodec_free_context(&mJpegCodecContext);
mJpegCodecContext = nullptr;
}

if (mJpegSwsContext) {
sws_freeContext(mJpegSwsContext);
}
} // Event::~Event()

void Event::createNotes(std::string &notes) {
Expand All @@ -241,20 +291,14 @@ void Event::addNote(const char *cause, const std::string &note) {
}

bool Event::WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame) const {
int thisquality =
(alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality)) ?
config.jpeg_alarm_file_quality : 0; // quality to use, zero is default

SystemTimePoint jpeg_timestamp = monitor->Exif() ? timestamp : SystemTimePoint();

if (!config.timestamp_on_capture) {
// stash the image we plan to use in another pointer regardless if timestamped.
// exif is only timestamp at present this switches on or off for write
Image ts_image(*image);
monitor->TimestampImage(&ts_image, timestamp);
return ts_image.WriteJpeg(event_file, thisquality, jpeg_timestamp);
return ts_image.WriteJpeg(event_file, mJpegCodecContext, mJpegSwsContext);
}
return image->WriteJpeg(event_file, thisquality, jpeg_timestamp);
return image->WriteJpeg(event_file, mJpegCodecContext, mJpegSwsContext);
}

bool Event::WritePacket(const std::shared_ptr<ZMPacket>packet) {
Expand Down
3 changes: 3 additions & 0 deletions src/zm_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ class Event {
std::string alarm_file;
VideoStore *videoStore;

AVCodecContext *mJpegCodecContext;
SwsContext *mJpegSwsContext;

std::string container;
std::string codec;
std::string video_file;
Expand Down
73 changes: 72 additions & 1 deletion src/zm_image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ Image::Image(const AVFrame *frame, int p_width, int p_height) :
static void dont_free(void *opaque, uint8_t *data) {
}

int Image::PopulateFrame(AVFrame *frame) {
int Image::PopulateFrame(AVFrame *frame) const {
Debug(1, "PopulateFrame: width %d height %d linesize %d colours %d imagesize %d %s",
width, height, linesize, colours, size,
av_get_pix_fmt_name(imagePixFormat)
Expand Down Expand Up @@ -1321,6 +1321,77 @@ bool Image::WriteJpeg(const std::string &filename,
return true;
}

bool Image::WriteJpeg(const std::string &filename,
AVCodecContext *p_jpegcodeccontext,
SwsContext *p_jpegswscontext) const {

if (config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8)) {
Image temp_image(*this);
temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
return temp_image.WriteJpeg(filename, p_jpegcodeccontext, p_jpegswscontext);
}

if (p_jpegcodeccontext == NULL) {
Error("Jpeg codec context is not initialized");
return false;
}

FILE *outfile = nullptr;
int raw_fd = 0;
av_frame_ptr frame = av_frame_ptr{zm_av_frame_alloc()};
AVPacket *pkt;

raw_fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (raw_fd < 0)
return false;
outfile = fdopen(raw_fd, "wb");
if (outfile == nullptr) {
close(raw_fd);
return false;
}

struct flock fl = { F_WRLCK, SEEK_SET, 0, 0, 0 };
if (fcntl(raw_fd, F_SETLKW, &fl) == -1) {
Error("Couldn't get lock on %s, continuing", filename.c_str());
}

if ( p_jpegswscontext ) {
av_frame_ptr temp_frame = av_frame_ptr{zm_av_frame_alloc()};
PopulateFrame(temp_frame.get());

frame.get()->width = width;
frame.get()->height = height;
frame.get()->format = AV_PIX_FMT_YUV420P;
av_image_fill_linesizes(frame.get()->linesize, AV_PIX_FMT_YUV420P, width);
av_frame_get_buffer(frame.get(), 32);

sws_scale(p_jpegswscontext, temp_frame.get()->data, temp_frame.get()->linesize, 0, height, frame.get()->data, frame.get()->linesize);

av_frame_unref(temp_frame.get());
} else {
PopulateFrame(frame.get());
}

pkt = av_packet_alloc();

avcodec_send_frame(p_jpegcodeccontext, frame.get());
if (avcodec_receive_packet(p_jpegcodeccontext, pkt) == 0) {
fwrite(pkt->data, 1, pkt->size, outfile);
av_packet_free(&pkt);
}

av_frame_unref(frame.get());

fl.l_type = F_UNLCK; /* set to unlock same region */
if (fcntl(raw_fd, F_SETLK, &fl) == -1) {
Error("Failed to unlock %s", filename.c_str());
}

fclose(outfile);

return true;
}

bool Image::DecodeJpeg(const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder) {
unsigned int new_width, new_height, new_colours, new_subpixelorder;

Expand Down
5 changes: 4 additions & 1 deletion src/zm_image.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ class Image {
const size_t buffer_size,
const int p_buffertype);

int PopulateFrame(AVFrame *frame);
int PopulateFrame(AVFrame *frame) const;

inline void CopyBuffer(const Image &image) {
Assign(image);
Expand Down Expand Up @@ -235,6 +235,9 @@ class Image {
const int &quality_override,
SystemTimePoint timestamp,
bool on_blocking_abort) const;
bool WriteJpeg(const std::string &filename,
AVCodecContext *p_jpegcodeccontext,
SwsContext *p_jpegswscontext) const;

bool DecodeJpeg(const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder);
bool EncodeJpeg(JOCTET *outbuffer, int *outbuffer_size, int quality_override=0) const;
Expand Down

0 comments on commit ddcc149

Please sign in to comment.