9 #include <libavcodec/avcodec.h>
10 #include <libswresample/swresample.h>
11 #include <libavformat/avformat.h>
12 #include <libavformat/avio.h>
13 #include <libavutil/avutil.h>
14 #include <libavutil/dict.h>
15 #include <libavutil/error.h>
16 #include <libavutil/frame.h>
17 #include <libavutil/pixfmt.h>
18 #include <libavutil/rational.h>
24 #include <sys/select.h>
33 #include "recording.pb-c.h"
39 #define LOG_TAG "grabber"
41 #define READ_BUFFER_SIZE 1024
61 static Display* s_display;
66 static char* s_path_results;
75 s_path_results = results;
84 switch (pthread_mutex_trylock(mtx))
87 LOGM(
"we got the lock, unlock and return 1 (true). -> NeedQuit Stop record");
88 pthread_mutex_unlock(mtx);
96 static void log_packet(
const AVFormatContext* fmt_ctx,
const AVPacket* pkt)
110 static int write_frame(AVFormatContext* fmt_ctx,
const AVRational* time_base, AVStream* st,
114 av_packet_rescale_ts(pkt, *time_base, st->time_base);
115 pkt->stream_index = st->index;
118 log_packet(fmt_ctx, pkt);
119 return av_interleaved_write_frame(fmt_ctx, pkt);
123 static void add_stream(
OutputStream* ost, AVFormatContext* oc, AVCodec** codec,
124 enum AVCodecID codec_id)
129 *codec = avcodec_find_encoder(codec_id);
132 fprintf(stderr,
"Could not find encoder for '%s'\n", avcodec_get_name(codec_id));
136 ost->
st = avformat_new_stream(oc, *codec);
139 fprintf(stderr,
"Could not allocate stream\n");
142 ost->
st->id = oc->nb_streams - 1;
145 switch ((*codec)->type)
147 case AVMEDIA_TYPE_VIDEO:
148 c->codec_id = codec_id;
150 c->bit_rate = 400000;
159 c->time_base = ost->
st->time_base;
163 if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO)
168 if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO)
182 if (oc->oformat->flags & AVFMT_GLOBALHEADER)
183 c->flags |= CODEC_FLAG_GLOBAL_HEADER;
189 static AVFrame* alloc_picture(
enum AVPixelFormat pix_fmt,
int width,
int height)
194 picture = av_frame_alloc();
198 picture->format = pix_fmt;
199 picture->width = width;
200 picture->height = height;
203 ret = av_frame_get_buffer(picture, 32);
206 fprintf(stderr,
"Could not allocate frame data.\n");
213 static void open_video(AVCodec* codec,
OutputStream* ost, AVDictionary* opt_arg)
216 AVCodecContext* c = ost->
st->codec;
217 AVDictionary* opt = NULL;
219 av_dict_copy(&opt, opt_arg, 0);
222 ret = avcodec_open2(c, codec, &opt);
226 fprintf(stderr,
"Could not open video codec: %s\n", av_err2str(ret));
231 ost->
frame = alloc_picture(c->pix_fmt, c->width, c->height);
234 fprintf(stderr,
"Could not allocate video frame\n");
242 if (c->pix_fmt != AV_PIX_FMT_YUV420P)
244 ost->
tmp_frame = alloc_picture(AV_PIX_FMT_YUV420P, c->width, c->height);
247 fprintf(stderr,
"Could not allocate temporary picture\n");
254 static void fill_yuv_image(AVFrame* pict,
int width,
int height)
262 ret = av_frame_make_writable(pict);
268 XGetImage(s_display, (Drawable)
g_window_id, 0, 0, width, height, AllPlanes, ZPixmap);
271 unsigned long red_mask = image->red_mask;
272 unsigned long green_mask = image->green_mask;
273 unsigned long blue_mask = image->blue_mask;
282 for (y = 0; y < height; y++)
284 for (x = 0; x < width; x++)
286 pixel = XGetPixel(image, x, y);
287 blue = pixel & blue_mask;
288 green = (pixel & green_mask) >> 8;
289 red = (pixel & red_mask) >> 16;
290 pict->data[0][y * pict->linesize[0] + x] =
291 RGB2Y(red, green, blue);
292 pict->data[1][(y / 2) * pict->linesize[1] + (x / 2)] =
RGB2U(red, green, blue);
293 pict->data[2][(y / 2) * pict->linesize[2] + (x / 2)] =
RGB2V(red, green, blue);
297 XDestroyImage(image);
300 static AVFrame* get_video_frame(
OutputStream* ost,
void* arg)
302 AVCodecContext* c = ost->
st->codec;
308 pthread_mutex_t* mx = arg;
314 if (c->pix_fmt != AV_PIX_FMT_YUV420P)
320 ost->
sws_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_YUV420P, c->width,
321 c->height, c->pix_fmt,
SCALE_FLAGS, NULL, NULL, NULL);
324 fprintf(stderr,
"Could not initialize the conversion context\n");
328 fill_yuv_image(ost->
tmp_frame, c->width, c->height);
334 fill_yuv_image(ost->
frame, c->width, c->height);
346 static int write_video_frame(AVFormatContext* oc,
OutputStream* ost,
void* arg)
355 frame = get_video_frame(ost, arg);
357 if (oc->oformat->flags & AVFMT_RAWPICTURE)
361 av_init_packet(&pkt);
366 pkt.flags |= AV_PKT_FLAG_KEY;
367 pkt.stream_index = ost->
st->index;
368 pkt.data = (uint8_t*) frame;
369 pkt.size =
sizeof(AVPicture);
371 pkt.pts = pkt.dts = frame->pts;
372 av_packet_rescale_ts(&pkt, c->time_base, ost->
st->time_base);
374 ret = av_interleaved_write_frame(oc, &pkt);
379 av_init_packet(&pkt);
382 ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);
385 fprintf(stderr,
"Error encoding video frame: %s\n", av_err2str(ret));
391 ret = write_frame(oc, &c->time_base, ost->
st, &pkt);
401 fprintf(stderr,
"Error while writing video frame: %s\n", av_err2str(ret));
405 return (frame || got_packet) ? 0 : 1;
410 avcodec_close(ost->
st->codec);
411 av_frame_free(&ost->
frame);
423 const char* filename;
426 AVCodec* video_codec;
429 int encode_video = 0;
430 AVDictionary* opt = NULL;
435 av_dict_set(&opt,
"author",
"aic", 0);
438 avformat_alloc_output_context2(&oc, NULL, NULL, filename);
441 LOGM(
"Could not deduce output format from file extension: using MPEG. %s",
443 avformat_alloc_output_context2(&oc, NULL,
"mpeg", filename);
452 if (fmt->video_codec != AV_CODEC_ID_NONE)
454 add_stream(&video_st, oc, &video_codec, fmt->video_codec);
462 open_video(video_codec, &video_st, opt);
464 av_dump_format(oc, 0, filename, 1);
467 if (!(fmt->flags & AVFMT_NOFILE))
469 ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);
472 fprintf(stderr,
"Could not open '%s': %s\n", filename, av_err2str(ret));
478 ret = avformat_write_header(oc, &opt);
481 fprintf(stderr,
"Error occurred when opening output file: %s\n", av_err2str(ret));
487 encode_video = !write_video_frame(oc, &video_st, (
void*) &args->
mtx);
494 av_write_trailer(oc);
498 close_stream(&video_st);
500 if (!(fmt->flags & AVFMT_NOFILE))
502 avio_closep(&oc->pb);
505 avformat_free_context(oc);
516 struct tm cur_localtime;
517 gettimeofday(&tv, NULL);
519 localtime_r(&curtime, &cur_localtime);
520 strftime(T, 30,
"%Y/%d/%m %H:%M:%S", &cur_localtime);
522 snprintf(usec, 4,
"%ld", tv.tv_usec);
524 unsigned char* img = NULL;
531 img = (
unsigned char*) malloc(3 * w * h);
533 LOGE(
"xgrabber(): out of memory");
534 memset(img, 0,
sizeof(*img));
537 snprintf(string1,
sizeof(string1),
"%s%s", T, usec);
540 GC gc = XCreateGC(s_display, (Drawable) g_window_id, 0, 0);
544 XDrawString(s_display, (Drawable) g_window_id, gc, 5,
g_height - 100 + 15, string1,
547 XImage* image = XGetImage(s_display, (Drawable) g_window_id, 0, 0, w, h, AllPlanes, ZPixmap);
550 unsigned long red_mask = image->red_mask;
551 unsigned long green_mask = image->green_mask;
552 unsigned long blue_mask = image->blue_mask;
556 for (x = 0; x < w; x++)
558 for (y = 0; y < h; y++)
560 unsigned long pixel = XGetPixel(image, x, y);
562 unsigned char blue = pixel & blue_mask;
563 unsigned char green = (pixel & green_mask) >> 8;
564 unsigned char red = (pixel & red_mask) >> 16;
566 img[(x + w * y) * 3 + 0] = blue;
567 img[(x + w * y) * 3 + 1] = green;
568 img[(x + w * y) * 3 + 2] = red;
572 XDestroyImage(image);
587 unsigned char* img = NULL;
594 img = (
unsigned char*) malloc(3 * w * h);
596 LOGE(
"grab_snapshot(): out of memory");
597 memset(img, 0,
sizeof(*img));
599 XImage* image = XGetImage(s_display, (Drawable)
g_window_id, 0, 0, w, h, AllPlanes, ZPixmap);
602 unsigned long red_mask = image->red_mask;
603 unsigned long green_mask = image->green_mask;
604 unsigned long blue_mask = image->blue_mask;
608 for (x = 0; x < w; x++)
610 for (y = 0; y < h; y++)
612 unsigned long pixel = XGetPixel(image, x, y);
614 unsigned char blue = pixel & blue_mask;
615 unsigned char green = (pixel & green_mask) >> 8;
616 unsigned char red = (pixel & red_mask) >> 16;
618 img[(x + w * y) * 3 + 0] = blue;
619 img[(x + w * y) * 3 + 1] = green;
620 img[(x + w * y) * 3 + 2] = red;
625 unsigned char bmpfileheader[14] = {
'B',
'M', 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0};
626 unsigned char bmpinfoheader[40] = {40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0};
627 unsigned char bmppad[3] = {0, 0, 0};
629 bmpfileheader[2] = (
unsigned char) (filesize);
630 bmpfileheader[3] = (
unsigned char) (filesize >> 8);
631 bmpfileheader[4] = (
unsigned char) (filesize >> 16);
632 bmpfileheader[5] = (
unsigned char) (filesize >> 24);
634 bmpinfoheader[4] = (
unsigned char) (w);
635 bmpinfoheader[5] = (
unsigned char) (w >> 8);
636 bmpinfoheader[6] = (
unsigned char) (w >> 16);
637 bmpinfoheader[7] = (
unsigned char) (w >> 24);
638 bmpinfoheader[8] = (
unsigned char) (h);
639 bmpinfoheader[9] = (
unsigned char) (h >> 8);
640 bmpinfoheader[10] = (
unsigned char) (h >> 16);
641 bmpinfoheader[11] = (
unsigned char) (h >> 24);
643 f = fopen(snap_filename,
"wb");
644 fwrite(bmpfileheader, 1, 14, f);
645 fwrite(bmpinfoheader, 1, 40, f);
652 fwrite(bmppad, 1, (4 - (
g_width * 3) % 4) % 4, f);
655 XDestroyImage(image);
665 pthread_mutex_lock(&args->
mtx);
669 FD_SET(args->
sock, &forread);
670 if (select(args->
sock + 1, &forread, 0, 0, 0) == -1)
672 LOGW(
" error select()");
675 if (FD_ISSET(args->
sock, &forread))
682 LOGW(
"select()1 %d", args->
len);
686 pthread_cond_signal(&args->
cond);
688 pthread_mutex_unlock(&args->
mtx);
690 struct timespec duration = {0, 250000};
691 nanosleep(&duration, NULL);
700 tv.tv_nsec = 100 * 1000000;
703 pthread_t pgrab_Thread;
705 RecordingPayload* recData;
707 struct stat st = {0};
708 char base_path[
BUF_SIZE] =
"./log/";
709 g_strlcpy(base_path, s_path_results,
sizeof(base_path));
710 LOGI(
"grabber base path: %s", base_path);
712 if (stat(base_path, &st) == -1)
714 mkdir(base_path, 0700);
719 pthread_mutex_lock(&args->
mtx);
722 pthread_cond_timedwait(&args->
cond, &args->
mtx, &tv);
728 recData = recording_payload__unpack(NULL, args->
len, args->
buffer);
730 LOGM(
" recData->recFilename=%s recData.startStop=%d ", recData->recfilename,
732 if (!strncmp(
"video", recData->recfilename, 5))
734 snprintf(grab_args.
record_filename,
sizeof(str_path),
"%s%s", base_path,
735 recData->recfilename);
739 pthread_mutex_init(&grab_args.
mtx, NULL);
740 pthread_mutex_lock(&grab_args.
mtx);
741 pthread_create(&pgrab_Thread, NULL, (
void*) &
ffmpeg_grabber, &grab_args);
746 pthread_mutex_unlock(&grab_args.
mtx);
747 pthread_join(pgrab_Thread, NULL);
751 else if (!strncmp(
"snap", recData->recfilename, 4) && recData->startstop == 2)
753 snprintf(str_path,
sizeof(str_path),
"%s%s", base_path, recData->recfilename);
758 pthread_mutex_unlock(&args->
mtx);
769 int flag_connect = 0;
771 uint8_t* read_buffer = (uint8_t*) malloc(
sizeof(uint8_t) *
READ_BUFFER_SIZE);
772 if (!r_args || !read_buffer)
773 LOGE(
"grab_handler_sock(): out of memory");
778 while (!flag_connect)
792 r_args->
buffer = read_buffer;
793 r_args->
sock = player_fd;
796 pthread_t pread_Thread1, pread_Thread2;
797 pthread_mutex_init(&r_args->
mtx, NULL);
798 pthread_create(&pread_Thread1, NULL, (
void*) &
precv, r_args);
799 pthread_create(&pread_Thread2, NULL, (
void*) &
pgrab, r_args);
806 amqp_envelope_t envelope;
810 RecordingPayload* recData;
814 struct stat st = {0};
815 char base_path[
BUF_SIZE] =
"./log/";
816 pthread_t pgrab_Thread;
818 if (stat(base_path, &st) == -1)
820 mkdir(base_path, 0700);
823 amqp_connection_state_t conn;
828 if (err_amqlisten == 0)
830 if (envelope.message.properties._flags & AMQP_BASIC_CONTENT_TYPE_FLAG)
832 recData = recording_payload__unpack(NULL, envelope.message.body.len,
833 envelope.message.body.bytes);
834 LOGM(
" recData->mpegFilename=%s recData.startStop=%d ", recData->recfilename,
836 if (!strncmp(
"video", recData->recfilename, 5))
839 base_path, recData->recfilename);
843 pthread_mutex_init(&grab_args.
mtx, NULL);
844 pthread_mutex_lock(&grab_args.
mtx);
845 pthread_create(&pgrab_Thread, NULL, (
void*) &
ffmpeg_grabber, &grab_args);
850 pthread_mutex_unlock(&grab_args.
mtx);
851 pthread_join(pgrab_Thread, NULL);
855 else if (!strncmp(
"snap", recData->recfilename, 4) && recData->startstop == 2)
858 snprintf(str_path,
sizeof(str_path),
"%s%s", base_path, recData->recfilename);
#define LOGD(...)
Log at DEBUG level.
A wrapper around a single output AVStream.
Utilities for consuming RabbitMQ messages.
int needQuit(pthread_mutex_t *mtx)
Parameter for sensor threads.
#define LOGE(...)
Log at ERROR level (makes the application abort)
#define BUF_SIZE
Small, fixed-size buffers.
void * g_window_id
Global variable for the X window id.
int g_height
Global variable for the window height.
void * grab_handler_amqp(void *args)
Struct holding the state of a recording (filename/lock)
int amqp_listen_retry(const char *hostname, int port, const char *bindingkey, amqp_connection_state_t *conn, const unsigned int tries)
Setup a consumer for a specific queue.
int socket_t
Alias to differenciate between regular ints and socket fds.
void grabber_set_display(Display *display)
Set the static X display pointer.
Shared structure between recv thread and grabber thread.
int ffmpeg_grabber(void *arg)
Function passed to a thread in order to detect format encoding - encode frame- and write in a file...
unsigned char * xgrabber()
socket_t open_socket(const char *ip, short port)
Connect to a host:port couple.
int g_width
Global variable for the window width.
char queue[BUF_SIZE]
Queue name.
Defines ports and structures for sensor threads.
#define STREAM_PIX_FMT
Pixel format of the stream (yuv420p)
struct SwrContext * swr_ctx
int amqp_consume(amqp_connection_state_t *conn, amqp_envelope_t *envelope)
Consume one message from a connection object.
#define STREAM_FRAME_RATE
FPS of the stream.
void * grab_handler_sock(void *args)
void grab_snapshot(char *snap_filename)
char record_filename[BUF_SIZE]
#define LOGM(...)
Log at MESSAGE level.
#define LOGW(...)
Log at WARNING level.
grabber records videos or snapshots from ampq messages
#define SOCKET_ERROR
Alias for the recv() return value in case of error.
Define common buffer sizes.
Define socket utilities to simplify networking.
struct SwsContext * sws_ctx
void grabber_set_path_results(char *results)
Set the static path to the screenshots/movies dir.
#define LOGI(...)
Log at INFO level.
const char * amqp_host
AMQP host.
int8_t flagRecording
Grabber-specific ??
#define PORT_GRAB
Port open on the VM.