/** * Function to wait in various ways (depending on settings) for the next frame * * @param state Pointer to the state data * @param [in][out] frame The last frame number, adjusted to next frame number on output * @return !0 if to continue, 0 if reached end of run */ static int wait_for_next_frame(RASPISTILLYUV_STATE *state, int *frame) { static int64_t complete_time = -1; int keep_running = 1; int64_t current_time = vcos_getmicrosecs64()/1000; if (complete_time == -1) complete_time = current_time + state->timeout; // if we have run out of time, flag we need to exit // If timeout = 0 then always continue if (current_time >= complete_time && state->timeout != 0) keep_running = 0; switch (state->frameNextMethod) { case FRAME_NEXT_SINGLE : // simple timeout for a single capture vcos_sleep(state->timeout); return 0; case FRAME_NEXT_FOREVER : { *frame+=1; // Have a sleep so we don't hog the CPU. vcos_sleep(10000); // Run forever so never indicate end of loop return 1; } case FRAME_NEXT_TIMELAPSE : { static int64_t next_frame_ms = -1; // Always need to increment by at least one, may add a skip later *frame += 1; if (next_frame_ms == -1) { vcos_sleep(state->timelapse); // Update our current time after the sleep current_time = vcos_getmicrosecs64()/1000; // Set our initial 'next frame time' next_frame_ms = current_time + state->timelapse; } else { int64_t this_delay_ms = next_frame_ms - current_time; if (this_delay_ms < 0) { // We are already past the next exposure time if (-this_delay_ms < -state->timelapse/2) { // Less than a half frame late, take a frame and hope to catch up next time next_frame_ms += state->timelapse; vcos_log_error("Frame %d is %d ms late", *frame, (int)(-this_delay_ms)); } else { int nskip = 1 + (-this_delay_ms)/state->timelapse; vcos_log_error("Skipping frame %d to restart at frame %d", *frame, *frame+nskip); *frame += nskip; this_delay_ms += nskip * state->timelapse; vcos_sleep(this_delay_ms); next_frame_ms += (nskip + 1) * state->timelapse; } } else { vcos_sleep(this_delay_ms); next_frame_ms += state->timelapse; } } return keep_running; } case FRAME_NEXT_KEYPRESS : { int ch; if (state->verbose) fprintf(stderr, "Press Enter to capture, X then ENTER to exit\n"); ch = getchar(); *frame+=1; if (ch == 'x' || ch == 'X') return 0; else { return keep_running; } } case FRAME_NEXT_IMMEDIATELY : { // Not waiting, just go to next frame. // Actually, we do need a slight delay here otherwise exposure goes // badly wrong since we never allow it frames to work it out // This could probably be tuned down. // First frame has a much longer delay to ensure we get exposure to a steady state if (*frame == 0) vcos_sleep(1000); else vcos_sleep(30); *frame+=1; return keep_running; } case FRAME_NEXT_GPIO : { // Intended for GPIO firing of a capture return 0; } case FRAME_NEXT_SIGNAL : { // Need to wait for a SIGUSR1 signal sigset_t waitset; int sig; int result = 0; sigemptyset( &waitset ); sigaddset( &waitset, SIGUSR1 ); // We are multi threaded because we use mmal, so need to use the pthread // variant of procmask to block SIGUSR1 so we can wait on it. pthread_sigmask( SIG_BLOCK, &waitset, NULL ); if (state->verbose) { fprintf(stderr, "Waiting for SIGUSR1 to initiate capture\n"); } result = sigwait( &waitset, &sig ); if (state->verbose) { if( result == 0) { fprintf(stderr, "Received SIGUSR1\n"); } else { fprintf(stderr, "Bad signal received - error %d\n", errno); } } *frame+=1; return keep_running; } } // end of switch // Should have returned by now, but default to timeout return keep_running; }
/** * buffer header callback function for encoder * * Callback will dump buffer data to the specific file * * @param port Pointer to port from which callback originated * @param buffer mmal buffer header pointer */ static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) { MMAL_BUFFER_HEADER_T *new_buffer; static int64_t base_time = -1; //fprintf(stderr,"*");fflush(stderr); // All our segment times based on the receipt of the first encoder callback if (base_time == -1) base_time = vcos_getmicrosecs64()/1000; // We pass our file handle and other stuff in via the userdata field. PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata; if (pData) { int bytes_written = buffer->length; int64_t current_time = vcos_getmicrosecs64()/1000; vcos_assert(pData->file_handle); if(pData->pstate->inlineMotionVectors) vcos_assert(pData->imv_file_handle); //else //{ // For segmented record mode, we need to see if we have exceeded our time/size, // but also since we have inline headers turned on we need to break when we get one to // ensure that the new stream has the header in it. If we break on an I-frame, the // SPS/PPS header is actually in the previous chunk. /*if ((buffer->flags & MMAL_BUFFER_HEADER_FLAG_CONFIG) && ((pData->pstate->segmentSize && current_time > base_time + pData->pstate->segmentSize) || (pData->pstate->splitWait && pData->pstate->splitNow))) { FILE *new_handle; base_time = current_time; pData->pstate->splitNow = 0; pData->pstate->segmentNumber++; // Only wrap if we have a wrap point set if (pData->pstate->segmentWrap && pData->pstate->segmentNumber > pData->pstate->segmentWrap) pData->pstate->segmentNumber = 1; new_handle = open_filename(pData->pstate); if (new_handle) { fclose(pData->file_handle); pData->file_handle = new_handle; } new_handle = open_imv_filename(pData->pstate); if (new_handle) { fclose(pData->imv_file_handle); pData->imv_file_handle = new_handle; } }*/ if (buffer->length) { //printf("writing..."); mmal_buffer_header_mem_lock(buffer); if(buffer->flags & MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO) { if(pData->pstate->inlineMotionVectors) { bytes_written = fwrite(buffer->data, 1, buffer->length, pData->imv_file_handle); } else { //We do not want to save inlineMotionVectors... bytes_written = buffer->length; } } else { bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle); } mmal_buffer_header_mem_unlock(buffer); if (bytes_written != buffer->length) { vcos_log_error("Failed to write buffer data (%d from %d)- aborting", bytes_written, buffer->length); pData->abort = 1; } } //} } else { vcos_log_error("Received a encoder buffer callback with no state"); } // release buffer back to the pool mmal_buffer_header_release(buffer); // and send one back to the port (if still open) if (port->is_enabled) { MMAL_STATUS_T status; new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue); if (new_buffer) status = mmal_port_send_buffer(port, new_buffer); if (!new_buffer || status != MMAL_SUCCESS) vcos_log_error("Unable to return a buffer to the encoder port"); } //printf("done.\n"); }