/// @brief Subtract and display time difference from clock_elapesed_begin(). /// /// - Notes: Accuracy is a function of timer resolution, CPU overhead and background tasks. /// /// @param[in] msg: User message to proceed Time display. /// /// @return void /// @see clock_gettime(). /// @see clock_elapesed_begin(). MEMSPACE void clock_elapsed_end(char *msg) { ts_t current; clock_gettime(0, (ts_t *) ¤t); subtract_timespec((ts_t *) ¤t, (ts_t *) &__clock_elapsed); if(msg && *msg) printf("[%s Time:%s]\n", msg, ts_to_str((ts_t *) ¤t) ); else printf("[Time:%s]\n", ts_to_str((ts_t *) ¤t) ); }
void *process_stream (void *args_) { // puts("_"); Thread_param *params = (Thread_param *) args_; if (params->delay_ms) { assert(params->delay_ms >= 0 && params->delay_ms <= 999); // fprinttfn(stderr, "gonna sleep %ims", params->delay_ms); timespec_ sleep_time; sleep_time.tv_sec = 0; sleep_time.tv_nsec = params->delay_ms * 1000000; assert(! nanosleep(&sleep_time, NULL)); } fprinttfn(stdout, "@starting_thread %i", params->id); // timespec_ start_time; // assert(clock_gettime(CLOCK_MONOTONIC, &start_time) == 0); AVFormatContext *av_context = NULL; // puts("(("); // fprintf(stderr, "str: %s\n", params->rtmp_string); // puts("))"); // pthread_mutex_lock(&mt); // puts("1"); int err = avformat_open_input(&av_context, params->rtmp_string, NULL, NULL); // puts("2"); // pthread_mutex_unlock(&mt); if (err != 0) { av_strerror(err, errbuf, ERRBUF_SZ); // fprintf(stderr, "avformat_open_input() failed: %s\n", errbuf); fprinttfn(stdout, "@error avformat_open_input() fail: %s", errbuf); // exit(EXIT_FAILURE); //TODO review fprinttfn(stdout, "@stopping_thread failed to open stream"); // avformat_close_input(&av_context); pthread_exit(NULL); } //***NetStream.Timestamp shows in log here already //*** it reads several frames // pthread_mutex_lock(&mt); err = avformat_find_stream_info(av_context, NULL); // pthread_mutex_unlock(&mt); if (err < 0) { av_strerror(err, errbuf, ERRBUF_SZ); fprintf(stderr, "avformat_find_stream_info() failed: %s\n", errbuf); exit(EXIT_FAILURE); } /* Dump information about file onto standard error */ // av_dump_format(av_context, 0, "t.flv", false); int videoStream = get_stream_index(av_context); // int buffered_frame_num = 0; bool is_first_frame = true; counter_thread_param_t cparam; AVPacket packet; av_init_packet(&packet); // timespec_ start_time; // assert(clock_gettime(CLOCK_MONOTONIC, &start_time) == 0); /* So we read the frames, accumulate the numbers and check passed time. When N seconds has passed, we subtract FPS * N from accumulated frame number implying a "player" consumes FPS and there should be >= FPS frames in buffer each second */ //"For video, the packet contains exactly one frame" //"This function returns what is stored in the file, and does not validate that what is there are valid frames for the decoder. //It will split what is stored in the file into frames and return one for each call. //It will not omit invalid data between valid frames so as to give the decoder the maximum information possible for decoding" while ((err = av_read_frame(av_context, &packet)) == 0) { if (packet.stream_index == videoStream) { if (is_first_frame) { is_first_frame = false; timespec_ first_frame_time = get_time(); timespec_ first_frame_latency = subtract_timespec(first_frame_time, connected_time); fprinttfn(stdout, "@first_frame %ld %ld", first_frame_latency.tv_sec, first_frame_latency.tv_nsec); // if (params->is_live && (! got_timestamp)) { // fprinttfn(stdout, "@error not_live"); // } pthread_t thr; pthread_mutex_init(&(cparam.mutex), NULL); cparam.frame_num = 0; cparam.should_stop = false; pthread_attr_t attributes; pthread_attr_init(&attributes); pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED); int r = pthread_create(&thr, &attributes, track_fps, (void *) &cparam); if (r) { fprintf(stderr, "failed to create track_fps thread: %i\n", r); exit(EXIT_FAILURE); } assert(pthread_attr_destroy(&attributes) == 0); } // timespec_ cur_time; // assert(clock_gettime(CLOCK_MONOTONIC, &cur_time) == 0); // timespec_ time_passed = subtract_timespec(cur_time, start_time); // fprinttfn(stdout, "@frame"); pthread_mutex_lock(&(cparam.mutex)); cparam.frame_num++; pthread_mutex_unlock(&(cparam.mutex)); // buffered_frame_num++; // if (time_passed.tv_sec > 0) { // buffered_frame_num -= time_passed.tv_sec * FPS; // //TODO !!! time_passed.tv_nsec // // fprinttfn(stdout, "@buffered_frame_num %i", buffered_frame_num); // start_time = cur_time; // // if (buffered_frame_num < 0) { // // fprinttfn(stdout, "@underrun %i", buffered_frame_num); // // } // } } av_free_packet(&packet); // av_init_packet(&packet); //? -- there's no call in http://libav.org/doxygen/master/pktdumper_8c_source.html but there is in some snippets } av_strerror(err, errbuf, ERRBUF_SZ); pthread_mutex_lock(&(cparam.mutex)); cparam.should_stop = true; pthread_mutex_unlock(&(cparam.mutex)); fprinttfn(stdout, "@stopping_thread %s", errbuf); //timeout in rtmp string leads to "End of file" avformat_close_input(&av_context); pthread_exit(NULL); }