39 #include <libcjson/cJSON.h> 42 #include <libavcodec/avcodec.h> 43 #include <libavutil/opt.h> 44 #include <libavutil/imgutils.h> 45 #include <libmediaprocsutils/log.h> 46 #include <libmediaprocsutils/check_utils.h> 47 #include <libmediaprocsutils/stat_codes.h> 48 #include <libmediaprocsutils/fifo.h> 49 #include <libmediaprocsutils/schedule.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 <libmediaprocsmuxers/live555_rtsp.h> 55 #include <libmediaprocscodecs/ffmpeg_x264.h> 56 #include <libmediaprocscodecs/ffmpeg_m2v.h> 57 #include <libmediaprocscodecs/ffmpeg_mp3.h> 58 #include <libmediaprocscodecs/ffmpeg_lhe.h> 62 #define LISTENING_PORT "8088" 63 #define LISTENING_HOST "127.0.0.1" 65 #define VIDEO_WIDTH "352" 66 #define VIDEO_HEIGHT "288" 68 #define REFRESH_EVENT (SDL_USEREVENT + 1) 69 #define BREAK_EVENT (SDL_USEREVENT + 2) 71 static volatile int flag_app_exit= 0;
80 volatile int flag_exit;
81 int enc_proc_id, dec_proc_id, mux_proc_id, dmux_proc_id;
82 int elem_strem_id_video_server;
83 const char *mime_setting_video;
87 static void prepare_and_send_raw_video_data(
procs_ctx_t *procs_ctx,
88 int enc_proc_id,
volatile int *ref_flag_exit)
90 uint8_t *p_data_y, *p_data_cr, *p_data_cb;
91 int64_t frame_period_usec, frame_period_90KHz;
93 const int width= atoi(VIDEO_WIDTH), height= atoi(VIDEO_HEIGHT);
96 const char *fps_cstr=
"30";
99 buf= (uint8_t*)malloc((width* height* 3)/ 2);
101 fprintf(stderr,
"Could not allocate producer raw data buffer\n");
104 proc_frame_ctx.
data= buf;
105 proc_frame_ctx.
p_data[0]= buf;
106 proc_frame_ctx.
p_data[1]= proc_frame_ctx.
p_data[0]+ (width* height);
107 proc_frame_ctx.
p_data[2]= proc_frame_ctx.
p_data[1]+ ((width* height)/4);
109 proc_frame_ctx.
width[1]= proc_frame_ctx.
linesize[1]= width>> 1;
110 proc_frame_ctx.
width[2]= proc_frame_ctx.
linesize[2]= width>> 1;
111 proc_frame_ctx.
height[0]= height;
112 proc_frame_ctx.
height[1]= height>> 1;
113 proc_frame_ctx.
height[2]= height>> 1;
115 proc_frame_ctx.
es_id= 0;
118 p_data_y= (uint8_t*)proc_frame_ctx.
p_data[0];
119 p_data_cr= (uint8_t*)proc_frame_ctx.
p_data[1];
120 p_data_cb= (uint8_t*)proc_frame_ctx.
p_data[2];
121 frame_period_usec= 1000000/ atoi(fps_cstr);
122 frame_period_90KHz= (frame_period_usec/1000)*
124 for(; *ref_flag_exit== 0;) {
126 usleep((
unsigned int)frame_period_usec);
127 proc_frame_ctx.
pts+= frame_period_90KHz;
130 for(y= 0; y< height; y++)
131 for(x= 0; x< width; x++)
132 p_data_y[y* proc_frame_ctx.
linesize[0]+ x]= x+ y+
133 proc_frame_ctx.
pts* 3;
135 for(y= 0; y< height>> 1; y++) {
136 for(x= 0; x< width>> 1; x++) {
137 p_data_cr[y* proc_frame_ctx.
linesize[1]+ x]= 128+ y+
138 proc_frame_ctx.
pts* 2;
139 p_data_cb[y* proc_frame_ctx.
linesize[2]+ x]= 64+ x+
140 proc_frame_ctx.
pts* 5;
163 fprintf(stderr,
"Bad argument '%s'\n", __FUNCTION__);
168 while(thr_ctx->flag_exit== 0) {
169 prepare_and_send_raw_video_data(thr_ctx->procs_ctx,
170 thr_ctx->enc_proc_id, &thr_ctx->flag_exit);
186 fprintf(stderr,
"Bad argument '%s'\n", __FUNCTION__);
191 while(thr_ctx->flag_exit== 0) {
195 if(proc_frame_ctx!= NULL)
199 if(ret_code!= STAT_SUCCESS) {
200 if(ret_code== STAT_EAGAIN)
203 fprintf(stderr,
"Error while encoding frame'\n");
211 if(proc_frame_ctx== NULL)
213 proc_frame_ctx->
es_id= thr_ctx->elem_strem_id_video_server;
216 if(ret_code!= STAT_SUCCESS) {
217 if(ret_code== STAT_EAGAIN)
220 fprintf(stderr,
"Error while multiplexing frame'\n");
225 if(proc_frame_ctx!= NULL)
235 int i, ret_code, elem_strem_id_video_client= -1;
236 int elementary_streams_cnt= 0;
239 char *rest_str= NULL;
240 cJSON *cjson_rest= NULL, *cjson_es_array= NULL, *cjson_aux= NULL;
244 fprintf(stderr,
"Bad argument '%s'\n", __FUNCTION__);
255 ret_code= STAT_EAGAIN;
256 while(ret_code!= STAT_SUCCESS && thr_ctx->flag_exit== 0) {
261 if(ret_code!= STAT_SUCCESS || proc_frame_ctx== NULL) {
262 fprintf(stderr,
"Error at line: %d\n", __LINE__);
267 ret_code=
procs_opt(thr_ctx->procs_ctx,
"PROCS_ID_GET",
268 thr_ctx->dmux_proc_id, &rest_str);
269 if(ret_code!= STAT_SUCCESS || rest_str== NULL) {
270 fprintf(stderr,
"Error at line: %d\n", __LINE__);
273 if((cjson_rest= cJSON_Parse(rest_str))== NULL) {
274 fprintf(stderr,
"Error at line: %d\n", __LINE__);
278 if((cjson_es_array= cJSON_GetObjectItem(cjson_rest,
279 "elementary_streams"))== NULL) {
280 fprintf(stderr,
"Error at line: %d\n", __LINE__);
284 elementary_streams_cnt= cJSON_GetArraySize(cjson_es_array);
285 for(i= 0; i< elementary_streams_cnt; i++) {
286 cJSON *cjson_es= cJSON_GetArrayItem(cjson_es_array, i);
287 if(cjson_es!= NULL) {
288 int elementary_stream_id;
290 const char *mime_needle= thr_ctx->mime_setting_video+
291 strlen(
"sdp_mimetype=");
294 cjson_aux= cJSON_GetObjectItem(cjson_es,
"elementary_stream_id");
295 if(cjson_aux== NULL) {
296 fprintf(stderr,
"Error at line: %d\n", __LINE__);
299 elementary_stream_id= cjson_aux->valueint;
302 cjson_aux= cJSON_GetObjectItem(cjson_es,
"sdp_mimetype");
303 if(cjson_aux== NULL) {
304 fprintf(stderr,
"Error at line: %d\n", __LINE__);
307 mime= cjson_aux->valuestring;
308 if(mime!= NULL && strcasecmp(mime_needle, mime)== 0)
309 elem_strem_id_video_client= elementary_stream_id;
312 free(rest_str); rest_str= NULL;
313 cJSON_Delete(cjson_rest); cjson_rest= NULL;
314 if(elem_strem_id_video_client< 0) {
315 fprintf(stderr,
"Error at line: %d\n", __LINE__);
322 if(ret_code!= STAT_SUCCESS)
323 fprintf(stderr,
"Error while decoding frame'\n");
326 while(thr_ctx->flag_exit== 0) {
329 if(proc_frame_ctx!= NULL)
333 if(ret_code!= STAT_SUCCESS) {
334 if(ret_code== STAT_EAGAIN)
337 fprintf(stderr,
"Error while de-multiplexing frame'\n");
342 if(proc_frame_ctx== NULL)
346 if(ret_code!= STAT_SUCCESS) {
347 if(ret_code== STAT_EAGAIN)
350 fprintf(stderr,
"Error while decoding frame'\n");
355 if(proc_frame_ctx!= NULL)
360 static void* consumer_thr_video(
void *t)
363 SDL_Window *sdlWindow= NULL;
364 SDL_Renderer *sdlRenderer= NULL;
365 const uint32_t pixformat= SDL_PIXELFORMAT_IYUV;
366 SDL_Texture *sdlTexture= NULL;
368 int w_Y= atoi(VIDEO_WIDTH),
369 h_Y= atoi(VIDEO_HEIGHT);
373 fprintf(stderr,
"Bad argument '%s'\n", __FUNCTION__);
379 if(SDL_Init(SDL_INIT_VIDEO)) {
380 fprintf(stderr,
"Could not initialize SDL: %s\n", SDL_GetError());
383 sdlWindow= SDL_CreateWindow(
"codecs_muxers_loopback.c",
384 SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w_Y, h_Y,
385 SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
387 fprintf(stderr,
"SDL: could not create window:%s\n",SDL_GetError());
390 sdlRenderer= SDL_CreateRenderer(sdlWindow, -1, 0);
392 fprintf(stderr,
"SDL: could not create renderer:%s\n",SDL_GetError());
395 sdlTexture= SDL_CreateTexture(sdlRenderer, pixformat,
396 SDL_TEXTUREACCESS_STREAMING, w_Y, h_Y);
398 fprintf(stderr,
"SDL: could not create texture:%s\n",SDL_GetError());
403 while(thr_ctx->flag_exit== 0) {
405 int w_Y_iput, h_Y_iput, ret_code;
408 if(proc_frame_ctx!= NULL)
412 if(ret_code!= STAT_SUCCESS) {
413 if(ret_code== STAT_EAGAIN)
416 fprintf(stderr,
"Error while receiving decoded frame'\n");
422 if(proc_frame_ctx== NULL) {
427 w_Y_iput= proc_frame_ctx->
width[0];
428 h_Y_iput= proc_frame_ctx->
height[0];
429 if(w_Y_iput<= 0 || h_Y_iput<= 0) {
430 fprintf(stderr,
"Invalid frame size\n");
435 if(w_Y_iput!= w_Y || h_Y_iput!= h_Y) {
441 event.type= SDL_WINDOWEVENT;
442 SDL_PushEvent(&event);
446 event.type= REFRESH_EVENT;
447 SDL_PushEvent(&event);
450 SDL_WaitEvent(&event);
451 if(event.type== REFRESH_EVENT) {
457 SDL_UpdateYUVTexture(sdlTexture, NULL,
461 SDL_RenderClear(sdlRenderer);
462 SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
463 SDL_RenderPresent(sdlRenderer);
465 }
else if(event.type==SDL_WINDOWEVENT){
467 SDL_SetWindowSize(sdlWindow, w_Y, h_Y);
468 }
else if(event.type==SDL_QUIT){
470 }
else if(event.type==BREAK_EVENT){
476 if(proc_frame_ctx!= NULL)
478 if(sdlTexture!= NULL)
479 SDL_DestroyTexture(sdlTexture);
480 if(sdlRenderer!= NULL) {
481 SDL_RenderPresent(sdlRenderer);
482 SDL_RenderClear(sdlRenderer);
483 SDL_DestroyRenderer(sdlRenderer);
486 SDL_DestroyWindow(sdlWindow);
491 static void http_event_handler(
struct mg_connection *c,
int ev,
void *p)
495 #define BODY_MAX 4096000 497 if(ev== MG_EV_HTTP_REQUEST) {
498 register size_t uri_len= 0, method_len= 0, qs_len= 0, body_len= 0;
499 const char *uri_p, *method_p, *qs_p, *body_p;
500 struct http_message *hm= (
struct http_message*)p;
501 char *url_str= NULL, *method_str= NULL, *str_response= NULL,
502 *qstring_str= NULL, *body_str= NULL;
505 if((uri_p= hm->uri.p)!= NULL && (uri_len= hm->uri.len)> 0 &&
507 url_str= (
char*)calloc(1, uri_len+ 1);
509 memcpy(url_str, uri_p, uri_len);
511 if((method_p= hm->method.p)!= NULL && (method_len= hm->method.len)> 0
512 && method_len< METH_MAX) {
513 method_str= (
char*)calloc(1, method_len+ 1);
514 if(method_str!= NULL)
515 memcpy(method_str, method_p, method_len);
517 if((qs_p= hm->query_string.p)!= NULL &&
518 (qs_len= hm->query_string.len)> 0 && qs_len< URI_MAX) {
519 qstring_str= (
char*)calloc(1, qs_len+ 1);
520 if(qstring_str!= NULL)
521 memcpy(qstring_str, qs_p, qs_len);
523 if((body_p= hm->body.p)!= NULL && (body_len= hm->body.len)> 0
524 && body_len< BODY_MAX) {
525 body_str= (
char*)calloc(1, body_len+ 1);
527 memcpy(body_str, body_p, body_len);
531 if(url_str!= NULL && method_str!= NULL)
533 qstring_str, method_str, body_str, body_len, &str_response);
535 if(str_response!= NULL && strlen(str_response)> 0) {
538 mg_printf(c,
"%s",
"HTTP/1.1 200 OK\r\n");
539 mg_printf(c,
"Content-Length: %d\r\n", (
int)strlen(str_response));
540 mg_printf(c,
"\r\n");
541 mg_printf(c,
"%s", str_response);
543 mg_printf(c,
"%s",
"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
546 if(str_response!= NULL)
550 if(method_str!= NULL)
552 if(qstring_str!= NULL)
556 }
else if(ev== MG_EV_RECV) {
557 mg_printf(c,
"%s",
"HTTP/1.1 202 ACCEPTED\r\nContent-Length: 0\r\n");
558 }
else if(ev== MG_EV_SEND) {
559 mg_printf(c,
"%s",
"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
569 struct mg_connection *c;
571 const char *listening_port= LISTENING_PORT;
572 struct mg_bind_opts opts;
573 const char *error_str= NULL;
577 fprintf(stderr,
"Bad argument '%s'\n", __FUNCTION__);
582 mg_mgr_init(&mgr, NULL);
584 memset(&opts, 0,
sizeof(opts));
585 opts.error_string= &error_str;
586 opts.user_data= thr_ctx;
587 c= mg_bind_opt(&mgr, listening_port, http_event_handler, opts);
589 fprintf(stderr,
"mg_bind_opt(%s:%s) failed: %s\n", LISTENING_HOST,
590 LISTENING_PORT, error_str);
593 mg_set_protocol_http_websocket(c);
595 while(flag_app_exit== 0)
596 mg_mgr_poll(&mgr, 1000);
602 static void stream_proc_quit_signal_handler()
604 printf(
"signaling application to finalize...\n"); fflush(stdout);
614 const char *proc_settings,
int *ref_proc_id)
617 char *rest_str= NULL;
618 cJSON *cjson_rest= NULL, *cjson_aux= NULL;
620 ret_code=
procs_opt(procs_ctx,
"PROCS_POST", proc_name, proc_settings,
622 if(ret_code!= STAT_SUCCESS || rest_str== NULL) {
623 fprintf(stderr,
"Error at line: %d\n", __LINE__);
626 if((cjson_rest= cJSON_Parse(rest_str))== NULL) {
627 fprintf(stderr,
"Error at line: %d\n", __LINE__);
630 if((cjson_aux= cJSON_GetObjectItem(cjson_rest,
"proc_id"))== NULL) {
631 fprintf(stderr,
"Error at line: %d\n", __LINE__);
634 if((*ref_proc_id= cjson_aux->valuedouble)< 0) {
635 fprintf(stderr,
"Error at line: %d\n", __LINE__);
638 free(rest_str); rest_str= NULL;
639 cJSON_Delete(cjson_rest); cjson_rest= NULL;
642 int main(
int argc,
char* argv[])
645 pthread_t producer_thread, mux_thread, dmux_thread, consumer_thread;
646 int ret_code, enc_proc_id= -1, dec_proc_id= -1, mux_proc_id= -1,
647 dmux_proc_id= -1, elem_strem_id_video_server= -1;
649 char *rest_str= NULL, *settings_str= NULL;
650 cJSON *cjson_rest= NULL, *cjson_aux= NULL;
652 const char *video_settings=
653 "width_output="VIDEO_WIDTH
654 "&height_output="VIDEO_HEIGHT;
659 const char *mime_setting=
"sdp_mimetype=video/mp2v";
664 const char *mime_setting=
"sdp_mimetype=video/mlhe";
669 const char *mime_setting=
"sdp_mimetype=video/avc1";
676 sigdelset(&
set, SIGINT);
677 pthread_sigmask(SIG_SETMASK, &
set, NULL);
678 signal(SIGINT, stream_proc_quit_signal_handler);
684 avcodec_register_all();
688 fprintf(stderr,
"Error at line: %d\n", __LINE__);
697 fprintf(stderr,
"Error at line: %d\n", __LINE__);
702 fprintf(stderr,
"Error at line: %d\n", __LINE__);
707 fprintf(stderr,
"Error at line: %d\n", __LINE__);
712 fprintf(stderr,
"Error at line: %d\n", __LINE__);
717 fprintf(stderr,
"Error at line: %d\n", __LINE__);
722 fprintf(stderr,
"Error at line: %d\n", __LINE__);
727 fprintf(stderr,
"Error at line: %d\n", __LINE__);
732 fprintf(stderr,
"Error at line: %d\n", __LINE__);
737 if((procs_ctx=
procs_open(NULL, 16, NULL, NULL))== NULL) {
738 fprintf(stderr,
"Error at line: %d\n", __LINE__);
752 ret_code=
procs_opt(procs_ctx,
"PROCS_ID_ES_MUX_REGISTER", mux_proc_id,
753 mime_setting, &rest_str);
754 if(ret_code!= STAT_SUCCESS || rest_str== NULL) {
755 fprintf(stderr,
"Error at line: %d\n", __LINE__);
758 if((cjson_rest= cJSON_Parse(rest_str))== NULL) {
759 fprintf(stderr,
"Error at line: %d\n", __LINE__);
762 if((cjson_aux= cJSON_GetObjectItem(cjson_rest,
"elementary_stream_id"))==
764 fprintf(stderr,
"Error at line: %d\n", __LINE__);
767 if((elem_strem_id_video_server= cjson_aux->valuedouble)< 0) {
768 fprintf(stderr,
"Error at line: %d\n", __LINE__);
771 free(rest_str); rest_str= NULL;
772 cJSON_Delete(cjson_rest); cjson_rest= NULL;
776 "rtsp_url=rtsp://127.0.0.1:8574/session", &dmux_proc_id);
781 thr_ctx.flag_exit= 0;
782 thr_ctx.enc_proc_id= enc_proc_id;
783 thr_ctx.dec_proc_id= dec_proc_id;
784 thr_ctx.mux_proc_id= mux_proc_id;
785 thr_ctx.elem_strem_id_video_server= elem_strem_id_video_server;
786 thr_ctx.mime_setting_video= mime_setting;
787 thr_ctx.dmux_proc_id= dmux_proc_id;
788 thr_ctx.procs_ctx= procs_ctx;
792 fprintf(stderr,
"Error at line: %d\n", __LINE__);
795 ret_code= pthread_create(&mux_thread, NULL,
mux_thr, &thr_ctx);
797 fprintf(stderr,
"Error at line: %d\n", __LINE__);
800 ret_code= pthread_create(&dmux_thread, NULL,
dmux_thr, &thr_ctx);
802 fprintf(stderr,
"Error at line: %d\n", __LINE__);
805 ret_code= pthread_create(&consumer_thread, NULL, consumer_thr_video,
808 fprintf(stderr,
"Error at line: %d\n", __LINE__);
813 printf(
"Starting server...\n"); fflush(stdout);
817 thr_ctx.flag_exit= 1;
818 ret_code=
procs_opt(procs_ctx,
"PROCS_ID_DELETE",
820 if(ret_code!= STAT_SUCCESS) {
821 fprintf(stderr,
"Error at line: %d\n", __LINE__);
824 ret_code=
procs_opt(procs_ctx,
"PROCS_ID_DELETE", dec_proc_id);
825 if(ret_code!= STAT_SUCCESS) {
826 fprintf(stderr,
"Error at line: %d\n", __LINE__);
829 ret_code=
procs_opt(procs_ctx,
"PROCS_ID_DELETE", dmux_proc_id);
830 if(ret_code!= STAT_SUCCESS) {
831 fprintf(stderr,
"Error at line: %d\n", __LINE__);
834 ret_code=
procs_opt(procs_ctx,
"PROCS_ID_DELETE", mux_proc_id);
835 if(ret_code!= STAT_SUCCESS) {
836 fprintf(stderr,
"Error at line: %d\n", __LINE__);
840 pthread_join(producer_thread, NULL);
841 pthread_join(mux_thread, NULL);
842 pthread_join(dmux_thread, NULL);
843 pthread_join(consumer_thread, NULL);
851 if(settings_str!= NULL)
853 if(cjson_rest!= NULL)
854 cJSON_Delete(cjson_rest);
size_t width[PROC_FRAME_NUM_DATA_POINTERS]
void proc_frame_ctx_release(proc_frame_ctx_t **ref_proc_frame_ctx)
static void * mux_thr(void *t)
int linesize[PROC_FRAME_NUM_DATA_POINTERS]
struct thr_ctx_s thr_ctx_t
int procs_opt(procs_ctx_t *procs_ctx, const char *tag,...)
const proc_if_t proc_if_ffmpeg_x264_enc
const proc_if_t proc_if_ffmpeg_m2v_enc
const proc_if_t proc_if_ffmpeg_mlhe_enc
int procs_module_open(log_ctx_t *log_ctx)
int procs_module_opt(const char *tag,...)
int procs_recv_frame(procs_ctx_t *procs_ctx, int proc_id, proc_frame_ctx_t **ref_proc_frame_ctx)
void procs_module_close()
static void * dmux_thr(void *t)
const proc_if_t proc_if_live555_rtsp_mux
int procs_send_frame(procs_ctx_t *procs_ctx, int proc_id, const proc_frame_ctx_t *proc_frame_ctx)
Planar YUV 4:2:0 with 12bpp (video)
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
static void * producer_thr_video(void *t)
const proc_if_t proc_if_ffmpeg_m2v_dec
const proc_if_t proc_if_live555_rtsp_dmux
size_t height[PROC_FRAME_NUM_DATA_POINTERS]
void procs_close(procs_ctx_t **ref_procs_ctx)
static void * http_server_thr(void *t)
const uint8_t * p_data[PROC_FRAME_NUM_DATA_POINTERS]
procs_ctx_t * procs_open(log_ctx_t *log_ctx, size_t max_procs_num, const char *prefix_name, const char *procs_href)
static void procs_post(procs_ctx_t *procs_ctx, const char *proc_name, const char *proc_settings, int *ref_proc_id)
const proc_if_t proc_if_ffmpeg_mlhe_dec