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.