MediaProcessors
utests_encdec1.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 Rafael Antoniello
3  *
4  * This file is part of MediaProcessors.
5  *
6  * MediaProcessors is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * MediaProcessors is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with MediaProcessors. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
27 #include <UnitTest++/UnitTest++.h>
28 #include <string>
29 
30 extern "C" {
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <pthread.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <math.h>
39 
40 #include <mongoose.h>
41 #include <libcjson/cJSON.h>
42 
43 #include <libavcodec/avcodec.h>
44 #include <libavutil/opt.h>
45 #include <libavutil/imgutils.h>
46 #include <libmediaprocsutils/log.h>
47 #include <libmediaprocsutils/check_utils.h>
48 #include <libmediaprocsutils/stat_codes.h>
49 #include <libmediaprocsutils/fifo.h>
50 #include <libmediaprocs/proc_if.h>
51 #include <libmediaprocs/procs.h>
52 #include <libmediaprocs/procs_api_http.h>
53 #include <libmediaprocs/proc.h>
54 #include "../src/ffmpeg_x264.h"
55 #include "../src/ffmpeg_m2v.h"
56 #include "../src/ffmpeg_mp3.h"
57 #include "../src/ffmpeg_lhe.h"
58 }
59 
60 #define SQR(x) ((x)*(x))
61 
62 #define LISTENING_PORT "8088"
63 #define LISTENING_HOST "127.0.0.1"
64 #define TEST_DURATION_SEC 8
65 #define MIN_PSNR_VAL 40
66 #define OUTPUT_FILE_VIDEO "/tmp/out.yuv"
67 #define OUTPUT_FILE_AUDIO "/tmp/out.wav"
68 #define MEDIA_TYPE_VIDEO 0
69 #define MEDIA_TYPE_AUDIO 1
70 
71 #define VIDEO_WIDTH "352"
72 #define VIDEO_HEIGHT "288"
73 
74 #define SETTINGS_SAMPLE_RATE "44100" // [samples/second]
75 #define SETTINGS_AUDIO_BITRATE "128000" // [bits/second]
76 
77 #define RESPONSE_BUF_SIZE 512*1024
78 
79 /* Debugging purposes: un-comment the next definition to save output raw
80  * YUV 4:2:0 video to a file (.yuv extension) and s16 interlaced audio
81  * (.wav extension).
82  * You can watch the output video file, for example, by using ffplay command:
83  * 'LD_LIBRARY_PATH=<...>/lib <...>/bin/ffplay -video_size 352x288 -framerate 10 /my/output/file.yuv'
84  * For the audio:
85  * 'LD_LIBRARY_PATH=<...>/lib <...>/bin/ffplay -f s16le -channels 2 /my/output/file.wav'
86  */
87 //#define WRITE_2_OFILE
88 
89 /* Implementation note: we do not change output with/height settings to be
90  * able to validate encoding-decoding loop using PSNR measurements (frames
91  * have to be of the same size to be easily compared).
92  */
93 static const char *test_settings_patterns[][2/*V/A*/][2]=
94 {
95  {
96  {
97  // [0]-> query string to PUT
98  "bit_rate_output=500000"
99  "&frame_rate_output=25"
100  "&width_output="VIDEO_WIDTH"&height_output="VIDEO_HEIGHT
101  "&gop_size=20"
102  "&conf_preset=medium"
103  ,
104  // [1]-> JSON extract string to compare when checking GET
105  "\"bit_rate_output\":500000,"
106  "\"frame_rate_output\":25,"
107  "\"width_output\":"VIDEO_WIDTH","
108  "\"height_output\":"VIDEO_HEIGHT","
109  "\"gop_size\":20,"
110  "\"conf_preset\":\"medium\""
111  },
112  {
113  "bit_rate_output=64000"
114  "&sample_rate_output=44100"
115  ,
116  "\"bit_rate_output\":64000,"
117  "\"sample_rate_output\":44100"
118  }
119  },
120  {
121  {
122  "bit_rate_output=300000"
123  "&frame_rate_output=25"
124  "&width_output="VIDEO_WIDTH"&height_output="VIDEO_HEIGHT
125  "&gop_size=10"
126  "&conf_preset=fast"
127  ,
128  "\"bit_rate_output\":300000,"
129  "\"frame_rate_output\":25,"
130  "\"width_output\":"VIDEO_WIDTH","
131  "\"height_output\":"VIDEO_HEIGHT","
132  "\"gop_size\":10,"
133  "\"conf_preset\":\"fast\""
134  },
135  {
136  "bit_rate_output=80000"
137  "&sample_rate_output=44100"
138  ,
139  "\"bit_rate_output\":80000,"
140  "\"sample_rate_output\":44100"
141  }
142  },
143  {
144  {
145  "bit_rate_output=700000"
146  "&frame_rate_output=25"
147  "&width_output="VIDEO_WIDTH"&height_output="VIDEO_HEIGHT
148  "&gop_size=36"
149  "&conf_preset=medium"
150  ,
151  "\"bit_rate_output\":700000,"
152  "\"frame_rate_output\":25,"
153  "\"width_output\":"VIDEO_WIDTH","
154  "\"height_output\":"VIDEO_HEIGHT","
155  "\"gop_size\":36,"
156  "\"conf_preset\":\"medium\""
157  },
158  {
159  "bit_rate_output=64000"
160  "&sample_rate_output=48000"
161  ,
162  "\"bit_rate_output\":64000,"
163  "\"sample_rate_output\":48000"
164  }
165  },
166  {
167  {
168  NULL, NULL
169  },
170  {
171  NULL, NULL
172  }
173  },
174 };
175 
176 typedef struct thr_ctx_s {
177  volatile int flag_exit;
178  volatile int flag_http_server_running;
179  int enc_proc_id, dec_proc_id;
180  int media_type;
181  int min_psnr_val;
182  procs_ctx_t *procs_ctx;
183 } thr_ctx_t;
184 
185 typedef struct ev_user_data_s {
186  volatile int flag_exit;
187  const char *ref_response_str;
188  procs_ctx_t *procs_ctx;
190 
191 static void http_client_event_handler(struct mg_connection *nc, int ev,
192  void *ev_data) {
193  struct http_message *hm= (struct http_message*)ev_data;
194  struct ev_user_data_s *data= (ev_user_data_t*)nc->user_data;
195  int connect_status;
196 
197  switch (ev) {
198  case MG_EV_CONNECT:
199  connect_status= *(int*)ev_data;
200  if (connect_status!= 0) {
201  printf("Error in HTTP connection: %s\n", strerror(connect_status));
202  data->flag_exit= 1;
203  }
204  break;
205  case MG_EV_HTTP_REPLY:
206  if(hm->body.len> 0 && hm->body.p!= NULL) {
207  //printf("Got reply-:\n%.*s (%d)\n", (int)hm->body.len,
208  // hm->body.p, (int)hm->body.len); //comment-me
209  if(hm->body.len< RESPONSE_BUF_SIZE) {
210  memcpy((void*)data->ref_response_str, hm->body.p, hm->body.len);
211  } else {
212  fprintf(stderr, "Message too big!'%s()'\n", __FUNCTION__);
213  exit(1);
214  }
215  }
216  nc->flags|= MG_F_SEND_AND_CLOSE;
217  data->flag_exit= 1;
218  break;
219  case MG_EV_CLOSE:
220  data->flag_exit= 1;
221  break;
222  default:
223  break;
224  }
225 }
226 
227 static char* http_client_request(const char *method, const char *url,
228  const char *qstring, const char *content)
229 {
230  size_t content_size;
231  struct mg_mgr mgr;
232  struct mg_connection *nc;
233  struct mg_connect_opts opts;
234  const char *error_str= NULL;
235  char response_buf[RESPONSE_BUF_SIZE]= {0};
236  struct ev_user_data_s ev_user_data= {0, response_buf, NULL};
237 
238  /* Check arguments.
239  * Note that 'content' may be NULL.
240  */
241  if(method== NULL || url== NULL) {
242  fprintf(stderr, "Bad argument '%s()'\n", __FUNCTION__);
243  exit(1);
244  }
245 
246  memset(&opts, 0, sizeof(opts));
247  opts.error_string= &error_str;
248  opts.user_data= &ev_user_data;
249 
250  mg_mgr_init(&mgr, NULL);
251  nc= mg_connect_opt(&mgr, LISTENING_HOST":"LISTENING_PORT,
252  http_client_event_handler, opts);
253  if(nc== NULL) {
254  fprintf(stderr, "mg_connect(%s:%s) failed: %s\n", LISTENING_HOST,
255  LISTENING_PORT, error_str);
256  exit(EXIT_FAILURE);
257  }
258  mg_set_protocol_http_websocket(nc);
259 
260  mg_printf(nc, "%s %s%s%s HTTP/1.0\r\n", method, url, qstring? "?": "",
261  qstring? qstring: "");
262  if(content!= NULL && (content_size= strlen(content))> 0) {
263  mg_printf(nc, "Content-Length: %d\r\n", (int)content_size);
264  mg_printf(nc, "\r\n");
265  mg_printf(nc, "%s", content);
266  } else {
267  mg_printf(nc, "\r\n");
268  }
269 
270  while(ev_user_data.flag_exit== 0)
271  mg_mgr_poll(&mgr, 1000);
272 
273  mg_mgr_free(&mgr);
274  //if(ev_user_data.ref_response_str!= NULL)
275  // printf("Got reply: '%s'\n",
276  // ev_user_data.ref_response_str); //comment-me
277  return strlen(response_buf)> 0? strdup(response_buf): NULL;
278 }
279 
280 static void http_event_handler(struct mg_connection *c, int ev, void *p)
281 {
282  if(ev== MG_EV_RECV) {
283  mg_printf(c, "%s", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
284 
285  } else if(ev == MG_EV_HTTP_REQUEST) {
286  register size_t uri_len= 0, method_len= 0, qs_len= 0, body_len= 0;
287  const char *uri_p, *method_p, *qs_p, *body_p;
288  struct http_message *hm= (struct http_message*)p;
289  char *url_str= NULL, *method_str= NULL, *str_response= NULL,
290  *qstring_str= NULL, *body_str= NULL;
291  thr_ctx_t *thr_ctx= (thr_ctx_t*)c->user_data;
292 
293  if((uri_p= hm->uri.p)!= NULL && (uri_len= hm->uri.len)> 0) {
294  url_str= (char*)calloc(1, uri_len+ 1);
295  memcpy(url_str, uri_p, uri_len);
296  }
297  if((method_p= hm->method.p)!= NULL && (method_len= hm->method.len)> 0) {
298  method_str= (char*)calloc(1, method_len+ 1);
299  memcpy(method_str, method_p, method_len);
300  }
301  if((qs_p= hm->query_string.p)!= NULL &&
302  (qs_len= hm->query_string.len)> 0) {
303  qstring_str= (char*)calloc(1, qs_len+ 1);
304  memcpy(qstring_str, qs_p, qs_len);
305  }
306  if((body_p= hm->body.p)!= NULL && (body_len= hm->body.len)> 0) {
307  body_str= (char*)calloc(1, body_len+ 1);
308  memcpy(body_str, body_p, body_len);
309  }
310 
311  /* Process HTTP request */
312  procs_api_http_req_handler(thr_ctx->procs_ctx, url_str, qstring_str,
313  method_str, body_str, body_len, &str_response);
314 
315  /* Send response */
316  mg_printf(c, "%s", "HTTP/1.1 200 OK\r\n");
317  if(str_response!= NULL && strlen(str_response)> 0) {
318  //printf("str_response: %s (len: %d)\n", str_response,
319  // (int)strlen(str_response)); //comment-me
320  mg_printf(c, "Content-Length: %d\r\n", (int)strlen(str_response));
321  mg_printf(c, "\r\n");
322  mg_printf(c, "%s", str_response);
323  } else {
324  mg_printf(c, "Content-Length: %d\r\n", 0);
325  }
326 
327  if(str_response!= NULL)
328  free(str_response);
329  if(url_str!= NULL)
330  free(url_str);
331  if(method_str!= NULL)
332  free(method_str);
333  if(qstring_str!= NULL)
334  free(qstring_str);
335  if(body_str!= NULL)
336  free(body_str);
337  }
338 }
339 
343 static void* http_server_thr(void *t)
344 {
345  struct mg_mgr mgr;
346  struct mg_connection *c;
347  thr_ctx_t *thr_ctx= (thr_ctx_t*)t;
348  const char *listening_port= LISTENING_PORT;
349  struct mg_bind_opts opts;
350  const char *error_str= NULL;
351 
352  /* Check argument */
353  if(thr_ctx== NULL) {
354  fprintf(stderr, "Bad argument 'enc_dec_thr()'\n");
355  exit(1);
356  }
357 
358  /* Create and configure the server */
359  mg_mgr_init(&mgr, NULL);
360 
361  memset(&opts, 0, sizeof(opts));
362  opts.error_string= &error_str;
363  opts.user_data= thr_ctx;
364  c= mg_bind_opt(&mgr, listening_port, http_event_handler, opts);
365  if(c== NULL) {
366  fprintf(stderr, "mg_bind_opt(%s:%s) failed: %s\n", LISTENING_HOST,
367  LISTENING_PORT, error_str);
368  exit(EXIT_FAILURE);
369  }
370  mg_set_protocol_http_websocket(c);
371 
372  /* Check argument */
373  if(thr_ctx== NULL) {
374  fprintf(stderr, "Bad argument 'enc_dec_thr()'\n");
375  exit(1);
376  }
377 
378  while(thr_ctx->flag_exit== 0) {
379  mg_mgr_poll(&mgr, 1000);
380  thr_ctx->flag_http_server_running= 1;
381  }
382 
383  mg_mgr_free(&mgr);
384  return NULL;
385 }
386 
390 static void* enc_dec_thr(void *t)
391 {
392  thr_ctx_t *thr_ctx= (thr_ctx_t*)t;
393  proc_frame_ctx_t *proc_frame_ctx= NULL;
394 
395  /* Check argument */
396  if(thr_ctx== NULL) {
397  fprintf(stderr, "Bad argument '%s()'\n", __FUNCTION__);
398  exit(1);
399  }
400 
401  while(thr_ctx->flag_exit== 0) {
402  int ret_code;
403 
404  /* Receive encoded frame */
405  if(proc_frame_ctx!= NULL)
406  proc_frame_ctx_release(&proc_frame_ctx);
407  ret_code= procs_recv_frame(thr_ctx->procs_ctx, thr_ctx->enc_proc_id,
408  &proc_frame_ctx);
409  if(ret_code!= STAT_SUCCESS) {
410  if(ret_code== STAT_EAGAIN)
411  usleep(1); // Avoid closed loops ("schedule")
412  else
413  fprintf(stderr, "Error while encoding frame'\n");
414  continue;
415  }
416 
417  /* Send encoded frame to decoder */
418  CHECK(proc_frame_ctx!= NULL);
419  ret_code= procs_send_frame(thr_ctx->procs_ctx, thr_ctx->dec_proc_id,
420  proc_frame_ctx);
421  if(ret_code!= STAT_SUCCESS) {
422  if(ret_code== STAT_EAGAIN)
423  usleep(1); // Avoid closed loops ("schedule")
424  else
425  fprintf(stderr, "Error while decoding frame'\n");
426  continue;
427  }
428  }
429 
430  if(proc_frame_ctx!= NULL)
431  proc_frame_ctx_release(&proc_frame_ctx);
432  return NULL;
433 }
434 
435 static void check_psnr(int width, int height, uint8_t *frame_original,
436  uint8_t *frame_processed, int min_psnr_val)
437 {
438  int i, y, x, pos_offset= 0;
439  int size= height* width;
440  double signal=0.0, noise=0.0, peak=0.0, mse;
441  double psnr1= 0;
442 
443  if(frame_original== NULL || frame_processed== NULL) {
444  CHECK(0);
445  return;
446  }
447 
448  /* YUV 4:2:0 */
449  for(i= 0; i< 3; i++) {
450  int w= width>>(i!= 0), h= height>>(i!= 0);
451  for(y= 0; y< h; y++) {
452  for(x= 0; x< w; x++) {
453  int pos= pos_offset+ y* w+ x;
454  signal+= SQR((double)frame_original[pos]);
455  noise+= SQR((double)frame_original[pos]-
456  (double)frame_processed[pos]);
457  if(peak< (double)frame_original[pos])
458  peak= (double)frame_original[pos];
459  }
460  }
461  pos_offset+= h* w;
462  }
463 
464  mse= noise/(double)size; // mean square error
465  psnr1= 10.0*log10(SQR(255.0)/mse);
466  //printf("MSE: %lf\n", mse);
467  //printf("SNR: %lf (dB)\n", 10.0*log10(signal/noise));
468  CHECK(psnr1> min_psnr_val);
469  if(psnr1<= min_psnr_val)
470  printf("PSNR(max=255): %lf (dB)\n", psnr1);
471  //printf("PSNR(max=%lf): %lf (dB)\n", peak, 10.0*log10(SQR(peak)/mse));
472 }
473 
474 static void treat_output_frame_video(proc_frame_ctx_t *proc_frame_ctx,
475  FILE **ref_file, int min_psnr_val)
476 {
477  int64_t pts;
478  int w_Y, h_Y, i, y, x, pos_dst_offset= 0;
479  uint8_t *frame_original= NULL, *frame_encdec= NULL;
480 
481  /* Check arguments */
482  if(proc_frame_ctx== NULL || ref_file== NULL)
483  return;
484 
485  w_Y= proc_frame_ctx->width[0];
486  h_Y= proc_frame_ctx->height[0];
487  frame_original= (uint8_t*)malloc((w_Y* h_Y* 3)>> 1);
488  frame_encdec= (uint8_t*)malloc((w_Y* h_Y* 3)>> 1);
489  if(frame_original== NULL || frame_encdec== NULL)
490  goto end;
491 
492  pts= proc_frame_ctx->pts;
493  for(i= 0; i< 3; i++) {
494  int w= proc_frame_ctx->width[i];
495  int h= proc_frame_ctx->height[i];
496  int stride= proc_frame_ctx->linesize[i];
497  for(y= 0; y< h; y++) {
498  for(x= 0; x< w; x++) {
499  int pos_src= y* stride+ x;
500  int pos_dst= pos_dst_offset+ y* w+ x;
501  int dec_val= proc_frame_ctx->p_data[i][pos_src];
502  int orig_val= (i== 0)? (x+ y+ pts* 3)& 0xFF:
503  (i== 1)? (128+ y+ pts* 2)& 0xFF:
504  (64+ x+ pts* 5)& 0xFF;
505 
506  /* Store YCrCb values to compute PSNR */
507  frame_original[pos_dst]= orig_val;
508  frame_encdec[pos_dst]= dec_val;
509  }
510  }
511  pos_dst_offset+= h* w;
512  }
513 
514  check_psnr(proc_frame_ctx->width[0], proc_frame_ctx->height[0],
515  frame_original, frame_encdec, min_psnr_val);
516 
517 #ifdef WRITE_2_OFILE
518  static FILE *file_raw= NULL;
519  if(file_raw== NULL) {
520  if((file_raw= fopen("/tmp/out_raw.yuv", "wb"))== NULL) {
521  fprintf(stderr, "Could not open %s\n", "/tmp/out_raw.yuv");
522  exit(1);
523  }
524  }
525  fwrite(frame_original, 1, pos_dst_offset, file_raw);
526  /* Open the output file if applicable */
527  if(*ref_file== NULL) {
528  if((*ref_file= fopen(OUTPUT_FILE_VIDEO, "wb"))== NULL) {
529  fprintf(stderr, "Could not open %s\n", OUTPUT_FILE_VIDEO);
530  exit(1);
531  }
532  }
533  fwrite(frame_encdec, 1, pos_dst_offset, *ref_file);
534 #endif
535 
536 end:
537  if(frame_original!= NULL) {
538  free(frame_original);
539  frame_original= NULL;
540  }
541  if(frame_encdec!= NULL) {
542  free(frame_encdec);
543  frame_encdec= NULL;
544  }
545 }
546 
547 static void treat_output_frame_audio(proc_frame_ctx_t *proc_frame_ctx,
548  FILE **ref_file, int min_psnr_val)
549 {
550  /* Check arguments */
551  if(proc_frame_ctx== NULL || ref_file== NULL)
552  return;
553 
554 #ifdef WRITE_2_OFILE
555  /* Open the output file if applicable */
556  if(*ref_file== NULL) {
557  if((*ref_file= fopen(OUTPUT_FILE_AUDIO, "wb"))== NULL) {
558  fprintf(stderr, "Could not open %s\n", OUTPUT_FILE_AUDIO);
559  exit(1);
560  }
561  }
562  // Write interlaced not planar
563  fwrite(proc_frame_ctx->p_data[0], 1, proc_frame_ctx->width[0], *ref_file);
564 #endif
565 }
566 
567 static void* consumer_thr(void *t)
568 {
569  thr_ctx_t *thr_ctx= (thr_ctx_t*)t;
570  proc_frame_ctx_t *proc_frame_ctx= NULL;
571  void(*treat_output_frame)(proc_frame_ctx_t*, FILE**, int)= NULL;
572  FILE *file= NULL;
573 
574  /* Check argument */
575  if(thr_ctx== NULL) {
576  fprintf(stderr, "Bad argument 'consumer_thr()'\n");
577  exit(1);
578  }
579 
580  /* Get function to treat output frame */
581  switch(thr_ctx->media_type) {
582  case MEDIA_TYPE_VIDEO:
583  treat_output_frame= treat_output_frame_video;
584  break;
585  case MEDIA_TYPE_AUDIO:
586  treat_output_frame= treat_output_frame_audio;
587  break;
588  default:
589  break;
590  }
591 
592  /* Output loop */
593  while(thr_ctx->flag_exit== 0) {
594  int ret_code;
595 
596  /* Receive decoded frame */
597  if(proc_frame_ctx!= NULL)
598  proc_frame_ctx_release(&proc_frame_ctx);
599  ret_code= procs_recv_frame(thr_ctx->procs_ctx, thr_ctx->dec_proc_id,
600  &proc_frame_ctx);
601  if(ret_code!= STAT_SUCCESS) {
602  if(ret_code== STAT_EAGAIN)
603  usleep(1); // Avoid closed loops ("schedule")
604  else
605  fprintf(stderr, "Error while receiving decoded frame'\n");
606  continue;
607  }
608  CHECK(proc_frame_ctx!= NULL);
609 
610  /* Write encoded-decoded frame to output */
611  if(proc_frame_ctx!= NULL && treat_output_frame!= NULL)
612  treat_output_frame(proc_frame_ctx, &file, thr_ctx->min_psnr_val);
613  }
614 
615  if(proc_frame_ctx!= NULL)
616  proc_frame_ctx_release(&proc_frame_ctx);
617  if(file!= NULL)
618  fclose(file);
619  return NULL;
620 }
621 
622 static void prepare_and_send_raw_video_data(procs_ctx_t *procs_ctx,
623  int enc_proc_id, volatile int *ref_flag_exit)
624 {
625  uint8_t *p_data_y, *p_data_cr, *p_data_cb;
626  int64_t frame_period_usec, frame_period_90KHz;
627  int x, y;
628  const int width= atoi(VIDEO_WIDTH), height= atoi(VIDEO_HEIGHT);
629  uint8_t *buf= NULL;
630  proc_frame_ctx_s proc_frame_ctx= {0};
631  const char *fps_cstr= "30";
632 
633  /* Prepare raw data buffer */
634  buf= (uint8_t*)malloc((width* height* 3)/ 2);
635  if(buf== NULL) {
636  CHECK(false);
637  goto end;
638  }
639  proc_frame_ctx.data= buf;
640  proc_frame_ctx.p_data[0]= buf;
641  proc_frame_ctx.p_data[1]= proc_frame_ctx.p_data[0]+ (width* height);
642  proc_frame_ctx.p_data[2]= proc_frame_ctx.p_data[1]+ ((width* height)/4);
643  proc_frame_ctx.width[0]= proc_frame_ctx.linesize[0]= width;
644  proc_frame_ctx.width[1]= proc_frame_ctx.linesize[1]= width>> 1;
645  proc_frame_ctx.width[2]= proc_frame_ctx.linesize[2]= width>> 1;
646  proc_frame_ctx.height[0]= height;
647  proc_frame_ctx.height[1]= height>> 1;
648  proc_frame_ctx.height[2]= height>> 1;
649  proc_frame_ctx.proc_sample_fmt= PROC_IF_FMT_YUV420P;
650 
651  /* Encode few seconds of video */
652  p_data_y= (uint8_t*)proc_frame_ctx.p_data[0];
653  p_data_cr= (uint8_t*)proc_frame_ctx.p_data[1];
654  p_data_cb= (uint8_t*)proc_frame_ctx.p_data[2];
655  frame_period_usec= 1000000/ atoi(fps_cstr); //usecs
656  frame_period_90KHz= (frame_period_usec/1000/*[msec]*/)*
657  90/*[ticks/msec]*/; //ticks
658  for(; *ref_flag_exit== 0;) {
659 
660  usleep((unsigned int)frame_period_usec); //comment-me
661  proc_frame_ctx.pts+= frame_period_90KHz;
662 
663  /* Y */
664  for(y= 0; y< height; y++)
665  for(x= 0; x< width; x++)
666  p_data_y[y* proc_frame_ctx.linesize[0]+ x]= x+ y+
667  proc_frame_ctx.pts* 3;
668  /* Cb and Cr */
669  for(y= 0; y< height>> 1; y++) {
670  for(x= 0; x< width>> 1; x++) {
671  p_data_cr[y* proc_frame_ctx.linesize[1]+ x]= 128+ y+
672  proc_frame_ctx.pts* 2;
673  p_data_cb[y* proc_frame_ctx.linesize[2]+ x]= 64+ x+
674  proc_frame_ctx.pts* 5;
675  }
676  }
677 
678  /* Encode the image */
679  procs_send_frame(procs_ctx, enc_proc_id, &proc_frame_ctx);
680  }
681 
682 end:
683  if(buf!= NULL) {
684  free(buf);
685  buf= NULL;
686  }
687 }
688 
689 static void prepare_and_send_raw_audio_data(procs_ctx_t *procs_ctx,
690  int enc_proc_id, volatile int *ref_flag_exit)
691 {
692  float sin_time, tincr;
693  int64_t frame_period_usec, frame_period_90KHz;
694  int frame_size_bytes, frame_size_samples, ret_code;
695  char *rest_str= NULL;
696  cJSON *cjson_rest= NULL, *cjson_aux= NULL;
697  uint8_t *buf= NULL;
698  proc_frame_ctx_s proc_frame_ctx= {0};
699  const char *sample_rate_cstr= SETTINGS_SAMPLE_RATE;
700 
701  /* Get frame size requested by audio encoder. We need this data to
702  * know how many bytes to send to the encoder.
703  */
704  ret_code= procs_opt(procs_ctx, "PROCS_ID_GET", enc_proc_id, &rest_str);
705  if(ret_code!= STAT_SUCCESS || rest_str== NULL) {
706  CHECK(false);
707  goto end;
708  }
709  //printf("PROCS_ID_GET: '%s'\n", rest_str); fflush(stdout); //comment-me
710  if((cjson_rest= cJSON_Parse(rest_str))== NULL) {
711  CHECK(false);
712  goto end;
713  }
714  if((cjson_aux= cJSON_GetObjectItem(cjson_rest,
715  "expected_frame_size_iput"))== NULL) {
716  CHECK(false);
717  goto end;
718  }
719  CHECK((frame_size_bytes= cjson_aux->valuedouble* sizeof(uint16_t))>= 0);
720  free(rest_str); rest_str= NULL;
721  cJSON_Delete(cjson_rest); cjson_rest= NULL;
722 
723  /* Allocate raw-data frame buffer */
724  buf= (uint8_t*)malloc(frame_size_bytes* 2); // stereo: two channels
725  if(buf== NULL) {
726  fprintf(stderr, "Could not allocate raw image buffer\n");
727  CHECK(false);
728  goto end;
729  }
730  frame_size_samples= frame_size_bytes>> 1; // 16-bit samples
731  proc_frame_ctx.data= buf;
732  proc_frame_ctx.p_data[0]= buf;
733  proc_frame_ctx.p_data[1]= buf+ frame_size_bytes;
734  proc_frame_ctx.width[0]= proc_frame_ctx.linesize[0]= frame_size_bytes;
735  proc_frame_ctx.width[1]= proc_frame_ctx.linesize[1]= frame_size_bytes;
736  proc_frame_ctx.height[0]= 1;
737  proc_frame_ctx.height[1]= 1;
738  proc_frame_ctx.proc_sample_fmt= PROC_IF_FMT_S16P;
739  proc_frame_ctx.pts= 0;
740 
741  /* Prepare raw data for a single 440Hz tone sound and send to encoder */
742  frame_period_usec= (1000000* frame_size_samples)/
743  atoi(sample_rate_cstr); //usecs
744  frame_period_90KHz= (frame_period_usec/1000/*[msec]*/)*
745  90/*[ticks/msec]*/; //ticks
746  sin_time= 0;
747  tincr= 2* M_PI* 440.0/ atoi(sample_rate_cstr);
748  for(; *ref_flag_exit== 0;) {
749  int j;
750  uint16_t *samples= (uint16_t*)proc_frame_ctx.data;
751 
752  usleep((unsigned int)frame_period_usec); //comment-me
753  proc_frame_ctx.pts+= frame_period_90KHz;
754 
755  for(j= 0; j< frame_size_samples; j++, sin_time+= tincr) {
756  int sample_val= (int)(sin(sin_time)* 10000);
757  samples[j]= sample_val; // right channel
758  samples[j+ frame_size_samples]= sample_val; // left channel
759  }
760  /* Encode the samples */
761  ret_code= procs_send_frame(procs_ctx, enc_proc_id, &proc_frame_ctx);
762  }
763 
764 end:
765  if(rest_str!= NULL)
766  free(rest_str);
767  if(cjson_rest!= NULL)
768  cJSON_Delete(cjson_rest);
769  if(buf!= NULL) {
770  free(buf);
771  buf= NULL;
772  }
773 }
774 
775 static void* producer_thr(void *t)
776 {
777  thr_ctx_t *thr_ctx= (thr_ctx_t*)t;
778  void(*prepare_and_send_raw_data)(procs_ctx_t *procs_ctx, int enc_proc_id,
779  volatile int *ref_flag_exit)= NULL;
780 
781  /* Check argument */
782  if(thr_ctx== NULL) {
783  fprintf(stderr, "Bad argument 'consumer_thr()'\n");
784  exit(1);
785  }
786 
787  /* Get function to treat output frame */
788  switch(thr_ctx->media_type) {
789  case MEDIA_TYPE_VIDEO:
790  prepare_and_send_raw_data= prepare_and_send_raw_video_data;
791  break;
792  case MEDIA_TYPE_AUDIO:
793  prepare_and_send_raw_data= prepare_and_send_raw_audio_data;
794  break;
795  default:
796  break;
797  }
798 
799  /* Producer loop */
800  while(thr_ctx->flag_exit== 0) {
801  usleep(1); // Kind-of schedule to avoid 100% CPU loops
802  prepare_and_send_raw_data(thr_ctx->procs_ctx, thr_ctx->enc_proc_id,
803  &thr_ctx->flag_exit);
804  }
805 
806  return NULL;
807 }
808 
809 static void encdec_loopback(const proc_if_t *proc_if_enc,
810  const proc_if_t *proc_if_dec, int media_type, int min_psnr_val)
811 {
812  pthread_t producer_thread, enc_dec_thread, consumer_thread,
813  http_server_thread;
814  int ret_code, enc_proc_id= -1, dec_proc_id= -1;
815  procs_ctx_t *procs_ctx= NULL;
816  char *rest_str= NULL, *settings_str= NULL;
817  cJSON *cjson_rest= NULL, *cjson_aux= NULL;
818  thr_ctx_s thr_ctx= {0};
819 
820  if(proc_if_enc== NULL || proc_if_dec== NULL) {
821  fprintf(stderr, "%s %d Bad arguments.\n", __FILE__, __LINE__);
822  return;
823  }
824 
825  /* Open LOG module */
826  log_module_open();
827 
828  /* Register all FFmpeg's CODECS */
829  avcodec_register_all();
830 
831  /* Open processors (PROCS) module */
832  ret_code= procs_module_open(NULL);
833  if(ret_code!= STAT_SUCCESS) {
834  CHECK(false);
835  goto end;
836  }
837 
838  /* Register encoder and decoder processor types */
839  ret_code= procs_module_opt("PROCS_REGISTER_TYPE", proc_if_enc);
840  if(ret_code!= STAT_SUCCESS) {
841  CHECK(false);
842  goto end;
843  }
844  ret_code= procs_module_opt("PROCS_REGISTER_TYPE", proc_if_dec);
845  if(ret_code!= STAT_SUCCESS) {
846  CHECK(false);
847  goto end;
848  }
849 
850  /* Get PROCS module's instance */
851  procs_ctx= procs_open(NULL, 16, NULL, NULL);
852  if(procs_ctx== NULL) {
853  CHECK(false);
854  goto end;
855  }
856 
857  /* Register (open) a encoder instance */
858  ret_code= procs_opt(procs_ctx, "PROCS_POST", proc_if_enc->proc_name, "",
859  &rest_str);
860  if(ret_code!= STAT_SUCCESS || rest_str== NULL) {
861  CHECK(false);
862  goto end;
863  }
864 
865  /* Parse response to get encoder Id. */
866  if((cjson_rest= cJSON_Parse(rest_str))== NULL) {
867  CHECK(false);
868  goto end;
869  }
870  if((cjson_aux= cJSON_GetObjectItem(cjson_rest, "proc_id"))== NULL) {
871  CHECK(false);
872  goto end;
873  }
874  CHECK((enc_proc_id= cjson_aux->valuedouble)>= 0);
875  free(rest_str); rest_str= NULL;
876  cJSON_Delete(cjson_rest); cjson_rest= NULL;
877 
878  /* Register (open) a decoder instance */
879  ret_code= procs_opt(procs_ctx, "PROCS_POST", proc_if_dec->proc_name, "",
880  &rest_str);
881  if(ret_code!= STAT_SUCCESS || rest_str== NULL) {
882  CHECK(false);
883  goto end;
884  }
885 
886  /* Parse response to get encoder Id. */
887  if((cjson_rest= cJSON_Parse(rest_str))== NULL) {
888  CHECK(false);
889  goto end;
890  }
891  if((cjson_aux= cJSON_GetObjectItem(cjson_rest, "proc_id"))== NULL) {
892  CHECK(false);
893  goto end;
894  }
895  CHECK((dec_proc_id= cjson_aux->valuedouble)>= 0);
896  free(rest_str); rest_str= NULL;
897  cJSON_Delete(cjson_rest); cjson_rest= NULL;
898 
899  /* Launch producer, encoding-decoding, consumer and HTTP-sever threads */
900  thr_ctx.flag_exit= 0;
901  thr_ctx.flag_http_server_running= 0;
902  thr_ctx.enc_proc_id= enc_proc_id;
903  thr_ctx.dec_proc_id= dec_proc_id;
904  thr_ctx.media_type= media_type;
905  thr_ctx.min_psnr_val= min_psnr_val;
906  thr_ctx.procs_ctx= procs_ctx;
907  ret_code= pthread_create(&producer_thread, NULL, producer_thr, &thr_ctx);
908  if(ret_code!= 0) {
909  CHECK(false);
910  goto end;
911  }
912  ret_code= pthread_create(&enc_dec_thread, NULL, enc_dec_thr, &thr_ctx);
913  if(ret_code!= 0) {
914  CHECK(false);
915  goto end;
916  }
917  ret_code= pthread_create(&consumer_thread, NULL, consumer_thr, &thr_ctx);
918  if(ret_code!= 0) {
919  CHECK(false);
920  goto end;
921  }
922  ret_code= pthread_create(&http_server_thread, NULL, http_server_thr,
923  &thr_ctx);
924  if(ret_code!= 0) {
925  CHECK(false);
926  goto end;
927  }
928 
929  /* Ugly (we should use signal and wait...) but practical */
930  while(thr_ctx.flag_http_server_running!= 1) {
931  usleep(1000*100);
932  }
933 
934  /* Change settings using API Rest through HTTP */
935  for(int i= 0; test_settings_patterns[i][media_type][0]!= NULL; i++) {
936  char *settings_str_p;
937  const char *query_str= test_settings_patterns[i][media_type][0];
938  const char *json_str= test_settings_patterns[i][media_type][1];
939 
940  /* Put new settings.
941  * (We know Id. 0 is the first instantiated processor: the encoder).
942  */
943  printf("\nNew settings\n"); //comment-me
944  printf("//------------------------//\n"); fflush(stdout); //comment-me
945  rest_str= http_client_request("PUT", "/procs/0.json", query_str, NULL);
946  if(ret_code!= STAT_SUCCESS) {
947  CHECK(false);
948  goto end;
949  }
950  if(rest_str!= NULL) {
951  free(rest_str); rest_str= NULL;
952  }
953 
954  /* Check new settings */
955  rest_str= http_client_request("GET", "/procs/0.json", NULL,
956  NULL);
957  if(ret_code!= STAT_SUCCESS || rest_str== NULL) {
958  CHECK(false);
959  goto end;
960  }
961  if((cjson_rest= cJSON_Parse(rest_str))== NULL) {
962  CHECK(false);
963  goto end;
964  }
965  if((cjson_aux= cJSON_GetObjectItem(cjson_rest, "data"))== NULL) {
966  CHECK(false);
967  goto end;
968  }
969  if((cjson_aux= cJSON_GetObjectItem(cjson_aux, "settings"))== NULL) {
970  CHECK(false);
971  goto end;
972  }
973  if(settings_str!= NULL) {
974  free(settings_str);
975  settings_str= NULL;
976  }
977  if((settings_str= cJSON_PrintUnformatted(cjson_aux))== NULL) {
978  CHECK(false);
979  goto end;
980  }
981  printf("Response (settings): '%s'\n", settings_str); //comment-me
982  fflush(stdout); //comment-me
983  /* Check response.
984  * Note we omit specific codec parameters -as response may be an
985  * extended settings version- and the closing '}' character
986  * (thus the '-1').
987  */
988  settings_str_p= strstr(settings_str, "\"bit_rate_output\"");
989  CHECK(strncmp(settings_str_p, json_str, strlen(json_str))== 0);
990  free(rest_str); rest_str= NULL;
991  cJSON_Delete(cjson_rest); cjson_rest= NULL;
992 
993  usleep(1000*1000*TEST_DURATION_SEC);
994  }
995 
996  /* Join the threads */
997  thr_ctx.flag_exit= 1;
998  ret_code= procs_opt(procs_ctx, "PROCS_ID_DELETE",
999  enc_proc_id); // before joining to unblock processor
1000  CHECK(ret_code== STAT_SUCCESS);
1001  ret_code= procs_opt(procs_ctx, "PROCS_ID_DELETE", dec_proc_id);
1002  CHECK(ret_code== STAT_SUCCESS);
1003  pthread_join(producer_thread, NULL);
1004  pthread_join(enc_dec_thread, NULL);
1005  pthread_join(consumer_thread, NULL);
1006  pthread_join(http_server_thread, NULL);
1007 
1008 end:
1009  if(procs_ctx!= NULL)
1010  procs_close(&procs_ctx);
1012  log_module_close();
1013  if(rest_str!= NULL)
1014  free(rest_str);
1015  if(settings_str!= NULL)
1016  free(settings_str);
1017  if(cjson_rest!= NULL)
1018  cJSON_Delete(cjson_rest);
1019 }
1020 
1021 SUITE(UTESTS_ENCODE_DECODE_1)
1022 {
1023  TEST(ENCODE_DECODE_X264)
1024  {
1026  MEDIA_TYPE_VIDEO, MIN_PSNR_VAL);
1027  }
1028  TEST(ENCODE_DECODE_MPEG2_VIDEO)
1029  {
1031  MEDIA_TYPE_VIDEO, MIN_PSNR_VAL);
1032  }
1033  TEST(ENCODE_DECODE_MLHE)
1034  {
1035  /* NOTE: For MLHE we still do not get all PSNR values above 15 dB, so
1036  * we treat it as a special case.
1037  */
1038  //encdec_loopback(&proc_if_ffmpeg_mlhe_enc, &proc_if_ffmpeg_mlhe_dec,
1039  // MEDIA_TYPE_VIDEO, 15/*MIN_PSNR_VAL*/);
1040  // Commented: by the moment LHE has some memory leaks
1041  }
1042  TEST(ENCODE_DECODE_MP3)
1043  {
1045  MEDIA_TYPE_AUDIO, MIN_PSNR_VAL);
1046  }
1047 }
1048 
size_t width[PROC_FRAME_NUM_DATA_POINTERS]
Definition: proc_if.h:113
const proc_if_t proc_if_ffmpeg_mp3_dec
Definition: ffmpeg_mp3.c:165
void proc_frame_ctx_release(proc_frame_ctx_t **ref_proc_frame_ctx)
Definition: proc_if.c:125
int linesize[PROC_FRAME_NUM_DATA_POINTERS]
Definition: proc_if.h:107
struct thr_ctx_s thr_ctx_t
SUITE(UTESTS_LIVE555_RTSP)
int procs_opt(procs_ctx_t *procs_ctx, const char *tag,...)
Definition: procs.c:474
int proc_sample_fmt
Definition: proc_if.h:127
const proc_if_t proc_if_ffmpeg_x264_enc
Definition: ffmpeg_x264.c:150
const proc_if_t proc_if_ffmpeg_m2v_enc
Definition: ffmpeg_m2v.c:145
const proc_if_t proc_if_ffmpeg_mp3_enc
Definition: ffmpeg_mp3.c:145
int procs_module_open(log_ctx_t *log_ctx)
Definition: procs.c:244
int procs_module_opt(const char *tag,...)
Definition: procs.c:294
int procs_recv_frame(procs_ctx_t *procs_ctx, int proc_id, proc_frame_ctx_t **ref_proc_frame_ctx)
Definition: procs.c:545
void procs_module_close()
Definition: procs.c:273
static void * http_server_thr(void *t)
int procs_send_frame(procs_ctx_t *procs_ctx, int proc_id, const proc_frame_ctx_t *proc_frame_ctx)
Definition: procs.c:504
Planar YUV 4:2:0 with 12bpp (video)
Definition: proc_if.h:55
int procs_api_http_req_handler(procs_ctx_t *procs_ctx, const char *url, const char *query_string, const char *request_method, char *content, size_t content_len, char **ref_str_response)
const proc_if_t proc_if_ffmpeg_x264_dec
Definition: ffmpeg_x264.c:170
const proc_if_t proc_if_ffmpeg_m2v_dec
Definition: ffmpeg_m2v.c:165
Planar signed 16 bits (typically audio)
Definition: proc_if.h:58
size_t height[PROC_FRAME_NUM_DATA_POINTERS]
Definition: proc_if.h:119
void procs_close(procs_ctx_t **ref_procs_ctx)
Definition: procs.c:417
const char * proc_name
Definition: proc_if.h:174
const uint8_t * p_data[PROC_FRAME_NUM_DATA_POINTERS]
Definition: proc_if.h:94
procs_ctx_t * procs_open(log_ctx_t *log_ctx, size_t max_procs_num, const char *prefix_name, const char *procs_href)
Definition: procs.c:336
uint8_t * data
Definition: proc_if.h:84
static void * enc_dec_thr(void *t)
int64_t pts
Definition: proc_if.h:138