ssize_t SDL_AMediaCodec_FakeFifo_dequeueOutputBuffer(SDL_AMediaCodec_FakeFifo *fifo, SDL_AMediaCodecBufferInfo *info, int64_t timeoutUs) { if (fifo->should_abort) return -1; int64_t timeoutMs = (timeoutUs + 999) / 1000; ssize_t dequeue_ret = -1; uint64_t wait_start = SDL_GetTickHR(); int64_t to_wait = timeoutMs; SDL_LockMutex(fifo->mutex); while (!fifo->should_abort) { if (fifo->size > 0) { SDL_AMediaCodec_FakeFrame *fake = &fifo->fakes[fifo->begin]; *info = fake->info; info->flags |= AMEDIACODEC__BUFFER_FLAG_FAKE_FRAME; dequeue_ret = fake->index; FAK_TRACE("%s, [%d]%lld", __func__, fifo->begin, info->presentationTimeUs); fifo->begin = (fifo->begin + 1) % FAKE_BUFFER_QUEUE_SIZE; fifo->size--; SDL_CondSignal(fifo->wakeup_enqueue_cond); break; } SDL_CondWaitTimeout(fifo->wakeup_dequeue_cond, fifo->mutex, to_wait); if (to_wait >= 0) { uint64_t now = SDL_GetTickHR(); if (now < wait_start) { // tick overflow dequeue_ret = -1; break; } else { uint64_t elapsed = now - wait_start; if (elapsed >= timeoutMs) { // timeout dequeue_ret = -1; break; } else { to_wait = timeoutMs - elapsed; } } } } SDL_UnlockMutex(fifo->mutex); if (fifo->should_abort) return -1; return dequeue_ret; }
int64_t SDL_ProfilerEnd(SDL_Profiler* profiler) { int64_t delta = SDL_GetTickHR() - profiler->begin_time; if (profiler->max_sample > 0) { profiler->total_elapsed += delta; profiler->total_counter += 1; profiler->sample_elapsed += delta; profiler->sample_counter += 1; if (profiler->sample_counter > profiler->max_sample) { profiler->sample_elapsed -= profiler->average_elapsed; profiler->sample_counter -= 1; } if (profiler->sample_counter > 0) { profiler->average_elapsed = profiler->sample_elapsed / profiler->sample_counter; } if (profiler->sample_elapsed > 0) { profiler->sample_per_seconds = profiler->sample_counter * 1000.f / profiler->sample_elapsed; } } return delta; }
float SDL_SpeedSamplerAdd(SDL_SpeedSampler *sampler, int enable_log, const char *log_tag) { Uint64 current = SDL_GetTickHR(); sampler->samples[sampler->next_index] = current; sampler->next_index++; sampler->next_index %= sampler->capacity; if (sampler->count + 1 >= sampler->capacity) { sampler->first_index++; sampler->first_index %= sampler->capacity; } else { sampler->count++; } if (sampler->count < 2) return 0; float samples_per_second = 1000.0f * (sampler->count - 1) / (current - sampler->samples[sampler->first_index]); if (enable_log && (sampler->last_log_time + 1000 < current || sampler->last_log_time > current)) { sampler->last_log_time = current; ALOGW("%s: %.2f\n", log_tag ? log_tag : "N/A", samples_per_second); } return samples_per_second; }
static int drain_output_buffer_l(JNIEnv *env, IJKFF_Pipenode *node, int64_t timeUs, int *dequeue_count) { IJKFF_Pipenode_Opaque *opaque = node->opaque; FFPlayer *ffp = opaque->ffp; int ret = 0; SDL_AMediaCodecBufferInfo bufferInfo; ssize_t output_buffer_index = 0; if (dequeue_count) *dequeue_count = 0; if (JNI_OK != SDL_JNI_SetupThreadEnv(&env)) { ALOGE("%s:create: SetupThreadEnv failed\n", __func__); return -1; } output_buffer_index = SDL_AMediaCodec_dequeueOutputBuffer(opaque->acodec, &bufferInfo, timeUs); if (output_buffer_index == AMEDIACODEC__INFO_OUTPUT_BUFFERS_CHANGED) { ALOGI("AMEDIACODEC__INFO_OUTPUT_BUFFERS_CHANGED\n"); // continue; } else if (output_buffer_index == AMEDIACODEC__INFO_OUTPUT_FORMAT_CHANGED) { ALOGI("AMEDIACODEC__INFO_OUTPUT_FORMAT_CHANGED\n"); SDL_AMediaFormat_deleteP(&opaque->output_aformat); opaque->output_aformat = SDL_AMediaCodec_getOutputFormat(opaque->acodec); if (opaque->output_aformat) { int width = 0; int height = 0; int color_format = 0; int stride = 0; int slice_height = 0; int crop_left = 0; int crop_top = 0; int crop_right = 0; int crop_bottom = 0; SDL_AMediaFormat_getInt32(opaque->output_aformat, "width", &width); SDL_AMediaFormat_getInt32(opaque->output_aformat, "height", &height); SDL_AMediaFormat_getInt32(opaque->output_aformat, "color-format", &color_format); SDL_AMediaFormat_getInt32(opaque->output_aformat, "stride", &stride); SDL_AMediaFormat_getInt32(opaque->output_aformat, "slice-height", &slice_height); SDL_AMediaFormat_getInt32(opaque->output_aformat, "crop-left", &crop_left); SDL_AMediaFormat_getInt32(opaque->output_aformat, "crop-top", &crop_top); SDL_AMediaFormat_getInt32(opaque->output_aformat, "crop-right", &crop_right); SDL_AMediaFormat_getInt32(opaque->output_aformat, "crop-bottom", &crop_bottom); // TI decoder could crash after reconfigure // ffp_notify_msg3(ffp, FFP_MSG_VIDEO_SIZE_CHANGED, width, height); // opaque->frame_width = width; // opaque->frame_height = height; ALOGI( "AMEDIACODEC__INFO_OUTPUT_FORMAT_CHANGED\n" " width-height: (%d x %d)\n" " color-format: (%s: 0x%x)\n" " stride: (%d)\n" " slice-height: (%d)\n" " crop: (%d, %d, %d, %d)\n" , width, height, SDL_AMediaCodec_getColorFormatName(color_format), color_format, stride, slice_height, crop_left, crop_top, crop_right, crop_bottom); } // continue; } else if (output_buffer_index == AMEDIACODEC__INFO_TRY_AGAIN_LATER) { AMCTRACE("AMEDIACODEC__INFO_TRY_AGAIN_LATER\n"); // continue; } else if (output_buffer_index < 0) { // enqueue packet as a fake picture PacketQueue *fake_q = &opaque->fake_pictq; SDL_LockMutex(fake_q->mutex); if (!fake_q->abort_request && fake_q->nb_packets <= 0) { SDL_CondWaitTimeout(fake_q->cond, fake_q->mutex, 1000); } SDL_UnlockMutex(fake_q->mutex); if (fake_q->abort_request) { ret = -1; goto fail; } else { AVPacket pkt; int dequeue_ret = ffp_packet_queue_get(&opaque->fake_pictq, &pkt, 0, &opaque->fake_pictq_serial); if (dequeue_ret < 0) { ret = -1; goto fail; } else if (dequeue_ret > 0) { if (!ffp_is_flush_packet(&pkt)) { if (dequeue_count) ++*dequeue_count; ret = amc_queue_picture_fake(node, &pkt); av_free_packet(&pkt); } ret = 0; goto fail; } } } else if (output_buffer_index >= 0) { ffp->vdps = SDL_SpeedSamplerAdd(&opaque->sampler, FFP_SHOW_VDPS_MEDIACODEC, "vdps[MediaCodec]"); if (dequeue_count) ++*dequeue_count; #ifdef FFP_SHOW_AMC_VDPS { if (opaque->benchmark_start_time == 0) { opaque->benchmark_start_time = SDL_GetTickHR(); } opaque->benchmark_frame_count += 1; if (0 == (opaque->benchmark_frame_count % 240)) { Uint64 diff = SDL_GetTickHR() - opaque->benchmark_start_time; double per_frame_ms = ((double) diff) / opaque->benchmark_frame_count; double fps = ((double) opaque->benchmark_frame_count) * 1000 / diff; ALOGE("%lf fps, %lf ms/frame, %"PRIu64" frames\n", fps, per_frame_ms, opaque->benchmark_frame_count); } } #endif #ifdef FFP_AMC_DISABLE_OUTPUT SDL_AMediaCodec_releaseOutputBuffer(opaque->acodec, output_buffer_index, false); goto done; #endif if (opaque->n_buf_out) { AMC_Buf_Out *buf_out; if (opaque->off_buf_out < opaque->n_buf_out) { // ALOGD("filling buffer... %d", opaque->off_buf_out); buf_out = &opaque->amc_buf_out[opaque->off_buf_out++]; buf_out->port = output_buffer_index; buf_out->info = bufferInfo; buf_out->pts = pts_from_buffer_info(node, &bufferInfo); sort_amc_buf_out(opaque->amc_buf_out, opaque->off_buf_out); } else { double pts; pts = pts_from_buffer_info(node, &bufferInfo); if (opaque->last_queued_pts != AV_NOPTS_VALUE && pts < opaque->last_queued_pts) { // ALOGE("early picture, drop!"); SDL_AMediaCodec_releaseOutputBuffer(opaque->acodec, output_buffer_index, false); goto done; } /* already sorted */ buf_out = &opaque->amc_buf_out[opaque->off_buf_out - 1]; /* new picture is the most aged, send now */ if (pts < buf_out->pts) { ret = amc_queue_picture_buffer(node, output_buffer_index, &bufferInfo); opaque->last_queued_pts = pts; // ALOGD("pts = %f", pts); } else { int i; /* find one to send */ for (i = opaque->off_buf_out - 1; i >= 0; i--) { buf_out = &opaque->amc_buf_out[i]; if (pts > buf_out->pts) { ret = amc_queue_picture_buffer(node, buf_out->port, &buf_out->info); opaque->last_queued_pts = buf_out->pts; // ALOGD("pts = %f", buf_out->pts); /* replace for sort later */ buf_out->port = output_buffer_index; buf_out->info = bufferInfo; buf_out->pts = pts_from_buffer_info(node, &bufferInfo); sort_amc_buf_out(opaque->amc_buf_out, opaque->n_buf_out); break; } } /* need to discard current buffer */ if (i < 0) { // ALOGE("buffer too small, drop picture!"); SDL_AMediaCodec_releaseOutputBuffer(opaque->acodec, output_buffer_index, false); goto done; } } } } else { ret = amc_queue_picture_buffer(node, output_buffer_index, &bufferInfo); } } done: ret = 0; fail: return ret; }
// like ff_ffplay.c: queue_picture() static int amc_queue_picture( IJKFF_Pipenode *node, SDL_AMediaCodec *acodec, int output_buffer_index, SDL_AMediaCodecBufferInfo *buffer_info, double pts, double duration, int64_t pos, int serial) { IJKFF_Pipenode_Opaque *opaque = node->opaque; FFPlayer *ffp = opaque->ffp; VideoState *is = ffp->is; Frame *vp; #if defined(DEBUG_SYNC) && 0 printf("frame_type=%c pts=%0.3f\n", av_get_picture_type_char(src_frame->pict_type), pts); #endif if (!(vp = ffp_frame_queue_peek_writable(&is->pictq))) return -1; vp->sar.num = 1; vp->sar.den = 1; /* alloc or resize hardware picture buffer */ if (!vp->bmp || vp->reallocate || !vp->allocated || vp->width != opaque->frame_width || vp->height != opaque->frame_height || !SDL_VoutOverlayAMediaCodec_isKindOf(vp->bmp)) { if (vp->width != opaque->frame_width || vp->height != opaque->frame_height) ffp_notify_msg3(ffp, FFP_MSG_VIDEO_SIZE_CHANGED, opaque->frame_width, opaque->frame_height); vp->allocated = 0; vp->reallocate = 0; vp->width = opaque->frame_width; vp->height = opaque->frame_height; /* the allocation must be done in the main thread to avoid locking problems. */ amc_alloc_picture(ffp); if (is->videoq.abort_request) return -1; } /* if the frame is not skipped, then display it */ if (vp->bmp) { /* get a pointer on the bitmap */ SDL_VoutLockYUVOverlay(vp->bmp); /* get a pointer on the bitmap */ if (SDL_VoutOverlayAMediaCodec_attachFrame(vp->bmp, opaque->acodec, output_buffer_index) < 0) { av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n"); exit(1); } /* update the bitmap content */ SDL_VoutUnlockYUVOverlay(vp->bmp); vp->pts = pts; vp->duration = duration; vp->pos = pos; vp->serial = serial; // ALOGE("vp %lf, %lf, %d, %d", pts, duration, (int)pos, (int)serial); /* now we can update the picture count */ ffp_frame_queue_push(&is->pictq); if (!is->viddec.first_frame_decoded) { ALOGD("MediaCodec/Video: first frame decoded\n"); is->viddec.first_frame_decoded_time = SDL_GetTickHR(); is->viddec.first_frame_decoded = 1; } } return 0; }
void SDL_ProfilerBegin(SDL_Profiler* profiler) { profiler->begin_time = SDL_GetTickHR(); }