]> rtime.felk.cvut.cz Git - frescor/streamer.git/blob - streamer.c
Attempt to fix unexpected video delays with 30 FPS
[frescor/streamer.git] / streamer.c
1 /*
2  *  Copyright (c) 2008 Luca Abeni
3  *
4  *  This is free software; see GPL.txt
5  */
6 #include <unistd.h>
7 #include <stdlib.h>
8 #include <signal.h>
9 #include <pthread.h>
10
11 #include <libavformat/avformat.h>
12 #include <libavdevice/avdevice.h>
13 #include <libswscale/swscale.h>
14
15 #include "input.h"
16 #include "output.h"
17 #include "codec.h"
18 #include "rt.h"
19
20 #include "streamer_config.h"
21
22 #ifdef CONFIG_OC_ULUT
23 #include <ul_log.h>
24 #include <ul_logreg.h>
25 #endif
26
27 #ifdef CONFIG_FFMPEG_WITH_FRSH
28 #include <frsh.h>
29
30 /*temporrary solution to pass network parameters */
31 extern long int frsh_rtp_budget, frsh_rtp_period_ms, frsh_rtp_deadline_ms;
32 #endif /*CONFIG_STREAMER_WITH_FRSH*/
33
34 static const char *sdp_file = "sdp.txt";
35 static const char *vdev = "/dev/video0";
36 static const char *dst = "127.0.0.1";
37 static int dport = 20000;
38 static int width = 320;
39 static int height = 240;
40 static int bitrate = 1000000;
41 int fps = 15;
42 static const char *impform = "video4linux2";
43 AVFormatContext *s, *os;
44
45 static void sdp_print(AVFormatContext *s, const char *fname)
46 {
47     char sdp[2048];
48     FILE *f;
49
50     f = fopen(fname, "w");
51     avf_sdp_create(&s, 1, sdp, sizeof(sdp));
52     fprintf(f, "%s\n", sdp);
53     fclose(f);
54 }
55
56 static void
57 usage(void)
58 {
59         printf("usage: streamer [ options ]\n");
60         printf("  -w <number>    send image width\n");
61         printf("  -h <number>    send image height\n");
62         printf("  -r <number>    refresh rate\n");
63         printf("  -r <path>      video device [%s]\n", vdev);
64         printf("  -m <addr>      destination IP address\n");
65         printf("  -i <string>    input video device format [%s]\n", impform);
66         printf("  -p <port>      destination port [%d]\n", dport);
67         printf("  -b <bitrate>   bitrate in b/s [%d]\n", bitrate);
68       #ifdef CONFIG_OC_ULUT
69         printf("  -l <number>|<domain>=<number>,...\n");
70       #endif /*CONFIG_OC_ULUT*/
71 }
72
73 static int args_parse(int argc, char *argv[])
74 {
75   int v;
76
77   while ((v = getopt(argc, argv, "w:h:r:d:m:i:l:b:p:")) >= 0) {
78     switch (v) {
79       case 'p':
80         dport = atoi(optarg);
81         break;
82       case 'w':
83         width = atoi(optarg);
84         break;
85       case 'h':
86         height = atoi(optarg);
87         break;
88       case 'r':
89         fps = atoi(optarg);
90         break;
91       case 'b':
92         bitrate = atoi(optarg);
93         break;
94       case 'd':
95         vdev = optarg;
96         break;
97       case 'm':
98         dst = optarg;
99         break;
100       case 'i':
101         impform = optarg;
102         if(!strcmp(impform, "v4l"))
103           impform = "video4linux";
104         else if(!strcmp(impform, "v4l2"))
105           impform = "video4linux2";
106         break;
107       #ifdef CONFIG_OC_ULUT
108       case 'l':
109         ul_log_domain_arg2levels(optarg);
110         break;
111       #endif /*CONFIG_OC_ULUT*/
112
113       default: /* unknown option */
114         fprintf(stderr, "%s: illegal option %c\n", argv[0], v);
115         usage();
116         exit(-1);
117     }
118   }
119
120   return 0;
121 }
122
123 int streamer_run_done_rq;
124
125 int
126 timespec_subtract (struct timespec *result,
127                    struct timespec *x,
128                    struct timespec *y)
129 {
130   /* Perform the carry for the later subtraction by updating Y. */
131   if (x->tv_nsec < y->tv_nsec) {
132     int num_sec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
133     y->tv_nsec -= 1000000000 * num_sec;
134     y->tv_sec += num_sec;
135   }
136   if (x->tv_nsec - y->tv_nsec > 1000000000) {
137     int num_sec = (x->tv_nsec - y->tv_nsec) / 1000000000;
138     y->tv_nsec += 1000000000 * num_sec;
139     y->tv_sec -= num_sec;
140   }
141
142   /* Compute the time remaining to wait.
143      `tv_nsec' is certainly positive. */
144   result->tv_sec = x->tv_sec - y->tv_sec;
145   result->tv_nsec = x->tv_nsec - y->tv_nsec;
146
147   /* Return 1 if result is negative. */
148   return x->tv_sec < y->tv_sec;
149 }
150
151
152 void* streamer_run(void* args)
153 {
154   int done;
155   int frame = 0;
156   int fps_avg = 0;
157   struct timespec start, end, d;
158   static unsigned max_size = 0, min_size = -1;
159   static double avg_size = 0;
160   clock_gettime(CLOCK_MONOTONIC, &start);
161   done = 0;
162   while (!(done = streamer_run_done_rq)) {
163     AVPacket *pkt;
164     pkt = read_input_packet(s);
165     if (pkt == NULL) {
166       done = 1;
167     } else {
168       AVFrame *f;
169       AVPacket *opkt;
170
171       pkt->pts += s->streams[pkt->stream_index]->start_time;
172       //rt_job_start(pkt->pts);
173       f = pkt_decode(s, pkt);
174       if (f) {
175         opkt = pkt_encode(os, f);
176         if (opkt) {
177           pkt_send(os, opkt);
178
179           clock_gettime(CLOCK_MONOTONIC, &end);
180           timespec_subtract(&d, &end, &start);
181           int fps_now = (1000<<8)/(d.tv_sec*1000+d.tv_nsec/1000000);
182           start = end;
183           fps_avg += (fps_now - fps_avg) >> 3;
184
185           if (opkt->size > max_size)
186                   max_size = opkt->size;
187           if (opkt->size < min_size)
188                   min_size = opkt->size;
189           avg_size = avg_size*frame/(frame+1) + (double)opkt->size/(frame+1);
190           printf("%5d: %2d fps  opkt size: %5d b  max=%5u b min=%5u b avg=%5.0f\n",
191                  frame,
192                  fps_avg>>8,
193                  opkt->size, max_size, min_size, avg_size);
194           if (frame % 100 == 0) max_size=0;
195           frame++;// = (frame + 1) % fps;
196         }
197       }
198       //rt_job_end();
199       av_free_packet(pkt);
200     }
201   }
202
203   return NULL;
204 }
205
206 void wait_for_ending_command(void) {
207   sigset_t sigset;
208   sigemptyset(&sigset);
209   sigaddset(&sigset, SIGINT);
210   sigaddset(&sigset, SIGTERM);
211   sigwaitinfo(&sigset, NULL);
212 }
213 static void block_signals(void) {
214   sigset_t sigset;
215   sigemptyset(&sigset);
216   sigaddset(&sigset, SIGINT);
217   sigaddset(&sigset, SIGTERM);
218   sigprocmask(SIG_BLOCK,&sigset,NULL);
219   pthread_sigmask(SIG_BLOCK,&sigset,NULL);
220 }
221
222
223 int main(int argc, char *argv[])
224 {
225
226   //long int cpu_budget, cpu_period;
227   int ret;
228
229   block_signals();
230
231 #ifdef CONFIG_FFMPEG_WITH_FRSH
232   ret = frsh_init();
233   if (ret) PERROR_AND_EXIT(ret, "frsh_init1");
234
235   /* fill default network contract params */
236   frsh_rtp_budget = 100*bitrate/8/100; 
237   frsh_rtp_period_ms = 1000;
238   frsh_rtp_deadline_ms = 1000/fps;
239 #endif /*CONFIG_FFMPEG_WITH_FRSH*/
240
241   avcodec_register_all();
242   av_register_all();
243   avdevice_register_all();
244
245   args_parse(argc, argv);
246
247   s = open_input_stream(vdev, width, height, fps, impform);
248   if (s == NULL) {
249     fprintf(stderr, "Cannot open input file %s\n", vdev);
250
251     return -1;
252   }
253   codec_open(s);
254   os = open_output_stream(dst, dport, CODEC_TYPE_VIDEO, fps);
255   if (os == NULL) {
256     fprintf(stderr, "Cannot open output stream\n");
257
258     return -1;
259   }
260   os->streams[0]->codec->width = s->streams[0]->codec->width;
261   os->streams[0]->codec->height = s->streams[0]->codec->height;
262   os->streams[0]->codec->time_base = s->streams[0]->codec->time_base;
263   os->streams[0]->codec->bit_rate = bitrate;
264   codec_connect(s->streams[0]->codec, os->streams[0]->codec);
265   out_codec_open(os);
266   dump_format(os, 0, os->filename, 1);
267   sdp_print(os, sdp_file);
268
269 #if CONFIG_STREAMER_WITH_FRSH && CONFIG_AQUOSA 
270   frsh_thread_attr_t frsh_attr;
271   frsh_thread_id_t thread;
272   frsh_vres_id_t cpu_vres;
273   frsh_contract_t cpu_contract;
274   frsh_rel_time_t cpu_budget, cpu_period;
275  
276   cpu_budget = fosa_msec_to_rel_time(50);
277   cpu_period = fosa_msec_to_rel_time(100);
278   /* Contract negotiation for CPU */
279   ret = frsh_contract_init(&cpu_contract);
280   if (ret) PERROR_AND_EXIT(ret, "CPU:frsh_contract_init");
281  
282   ret = frsh_contract_set_basic_params(&cpu_contract,
283                                              &cpu_budget,
284                                              &cpu_period,
285                                              FRSH_WT_BOUNDED,
286                                              FRSH_CT_REGULAR);
287         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_basic_params");
288         ret = frsh_contract_set_resource_and_label(&cpu_contract, 
289                         FRSH_RT_PROCESSOR, FRSH_CPU_ID_DEFAULT, "aqcpu_cont");
290         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_set_resource_and_label");
291
292         ret = frsh_contract_negotiate(&cpu_contract, &cpu_vres);
293         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_negotiate");
294         printf("Aqcpu vres negotiated\n");
295
296         pthread_attr_init(&frsh_attr);
297         ret = frsh_thread_create_and_bind(cpu_vres, &thread, &frsh_attr, 
298                                 streamer_run, (void*) NULL);
299         if (ret) PERROR_AND_EXIT(ret, "frsh_thread_create_and_bind");
300
301         wait_for_ending_command();
302
303         streamer_run_done_rq = 1;
304         
305         pthread_join(thread.pthread_id, (void**) NULL); 
306
307         printf("Ending contracts\n");
308
309         ret = frsh_contract_cancel(cpu_vres);
310         if (ret) PERROR_AND_EXIT(ret, "frsh_contract_cancel");
311
312         printf("Finishing\n");
313
314         close_output_stream(os);
315 #else
316         pthread_attr_t attr;
317         pthread_t streamer_th;
318
319         pthread_attr_init(&attr);
320
321         ret = pthread_create(&streamer_th, &attr, streamer_run, (void*) NULL);
322         if (ret) 
323                         printf("Failed to create streamer thread\n.");
324
325         wait_for_ending_command();
326
327         streamer_run_done_rq = 1;
328
329         pthread_join(streamer_th, (void**) NULL);
330
331         printf("Finishing\n");
332
333         close_output_stream(os);
334 #endif
335
336   return 0;
337 }
338