void *VideoStream::StreamingThreadCallback(void *ctx){ Debug( 1, "StreamingThreadCallback started" ); if (ctx == NULL) return NULL; VideoStream* videoStream = reinterpret_cast<VideoStream*>(ctx); const uint64_t nanosecond_multiplier = 1000000000; uint64_t target_interval_ns = nanosecond_multiplier * ( ((double)videoStream->ost->codec->time_base.num) / (videoStream->ost->codec->time_base.den) ); uint64_t frame_count = 0; timespec start_time; clock_gettime(CLOCK_MONOTONIC, &start_time); uint64_t start_time_ns = (start_time.tv_sec*nanosecond_multiplier) + start_time.tv_nsec; while(videoStream->do_streaming) { timespec current_time; clock_gettime(CLOCK_MONOTONIC, ¤t_time); uint64_t current_time_ns = (current_time.tv_sec*nanosecond_multiplier) + current_time.tv_nsec; uint64_t target_ns = start_time_ns + (target_interval_ns * frame_count); if ( current_time_ns < target_ns ) { // It's not time to render a frame yet. usleep( (target_ns - current_time_ns) * 0.001 ); } // By sending the last rendered frame we deliver frames to the client more accurate. // If we're encoding the frame before sending it there will be lag. // Since this lag is not constant the client may skip frames. // Get the last rendered packet. AVPacket *packet = videoStream->packet_buffers[videoStream->packet_index]; if (packet->size) { videoStream->SendPacket(packet); } av_free_packet(packet); videoStream->packet_index = videoStream->packet_index ? 0 : 1; // Lock buffer and render next frame. if ( pthread_mutex_lock( videoStream->buffer_copy_lock ) != 0 ) { Fatal( "StreamingThreadCallback: pthread_mutex_lock failed." ); } if ( videoStream->buffer_copy ) { // Encode next frame. videoStream->ActuallyEncodeFrame( videoStream->buffer_copy, videoStream->buffer_copy_used, videoStream->add_timestamp, videoStream->timestamp ); } if ( pthread_mutex_unlock( videoStream->buffer_copy_lock ) != 0 ) { Fatal( "StreamingThreadCallback: pthread_mutex_unlock failed." ); } frame_count++; } return 0; }