bool CMMALVideo::SendCodecConfigData() { MMAL_STATUS_T status; if (!m_dec_input_pool) return true; // send code config data MMAL_BUFFER_HEADER_T *buffer = mmal_queue_timedwait(m_dec_input_pool->queue, 500); if (!buffer) { CLog::Log(LOGERROR, "%s::%s - mmal_queue_get failed", CLASSNAME, __func__); return false; } mmal_buffer_header_reset(buffer); buffer->cmd = 0; buffer->length = std::min(m_hints.extrasize, buffer->alloc_size); memcpy(buffer->data, m_hints.extradata, buffer->length); buffer->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_CONFIG; if (g_advancedSettings.CanLogComponent(LOGVIDEO)) CLog::Log(LOGDEBUG, "%s::%s - %-8p %-6d flags:%x", CLASSNAME, __func__, buffer, buffer->length, buffer->flags); status = mmal_port_send_buffer(m_dec_input, buffer); if (status != MMAL_SUCCESS) { CLog::Log(LOGERROR, "%s::%s Failed send buffer to decoder input port (status=%x %s)", CLASSNAME, __func__, status, mmal_status_to_string(status)); return false; } return true; }
int CMMALRenderer::GetImage(YV12Image *image, int source, bool readonly) { #if defined(MMAL_DEBUG_VERBOSE) CLog::Log(LOGDEBUG, "%s::%s - %p %d %d", CLASSNAME, __func__, image, source, readonly); #endif if (!image) return -1; if( source < 0) return -1; if (m_format == RENDER_FMT_MMAL) { } else if (m_format == RENDER_FMT_YUV420P) { const int pitch = ALIGN_UP(m_sourceWidth, 32); const int aligned_height = ALIGN_UP(m_sourceHeight, 16); MMAL_BUFFER_HEADER_T *buffer = mmal_queue_timedwait(m_vout_input_pool->queue, 500); if (!buffer) { CLog::Log(LOGERROR, "%s::%s - mmal_queue_get failed", CLASSNAME, __func__); return -1; } mmal_buffer_header_reset(buffer); buffer->length = 3 * pitch * aligned_height >> 1; assert(buffer->length <= buffer->alloc_size); image->width = m_sourceWidth; image->height = m_sourceHeight; image->flags = 0; image->cshift_x = 1; image->cshift_y = 1; image->bpp = 1; image->stride[0] = pitch; image->stride[1] = image->stride[2] = pitch>>image->cshift_x; image->planesize[0] = pitch * aligned_height; image->planesize[1] = image->planesize[2] = (pitch>>image->cshift_x)*(aligned_height>>image->cshift_y); image->plane[0] = (uint8_t *)buffer->data; image->plane[1] = image->plane[0] + image->planesize[0]; image->plane[2] = image->plane[1] + image->planesize[1]; CLog::Log(LOGDEBUG, "%s::%s - %p %d", CLASSNAME, __func__, buffer, source); YUVBUFFER &buf = m_buffers[source]; memset(&buf, 0, sizeof buf); buf.mmal_buffer = buffer; } else assert(0);
CMMALRenderer::CMMALRenderer() : CThread("CMMALRenderer") { CLog::Log(LOGDEBUG, "%s::%s", CLASSNAME, __func__); m_vout = NULL; m_vout_input = NULL; m_vout_input_pool = NULL; memset(m_buffers, 0, sizeof m_buffers); mmal_buffer_header_reset(&m_quit_packet); m_release_queue = mmal_queue_create(); m_iYV12RenderBuffer = 0; Create(); }
// Move prepared/split packets from waiting_buffers to the MMAL decoder. static int ffmmal_fill_input_port(AVCodecContext *avctx) { MMALDecodeContext *ctx = avctx->priv_data; while (ctx->waiting_buffers) { MMAL_BUFFER_HEADER_T *mbuffer; FFBufferEntry *buffer; MMAL_STATUS_T status; mbuffer = mmal_queue_get(ctx->pool_in->queue); if (!mbuffer) return 0; buffer = ctx->waiting_buffers; mmal_buffer_header_reset(mbuffer); mbuffer->cmd = 0; mbuffer->pts = buffer->pts; mbuffer->dts = buffer->dts; mbuffer->flags = buffer->flags; mbuffer->data = buffer->data; mbuffer->length = buffer->length; mbuffer->user_data = buffer; mbuffer->alloc_size = ctx->decoder->input[0]->buffer_size; // Remove from start of the list ctx->waiting_buffers = buffer->next; if (ctx->waiting_buffers_tail == buffer) ctx->waiting_buffers_tail = NULL; if ((status = mmal_port_send_buffer(ctx->decoder->input[0], mbuffer))) { mmal_buffer_header_release(mbuffer); av_buffer_unref(&buffer->ref); if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) avpriv_atomic_int_add_and_fetch(&ctx->packets_buffered, -1); av_free(buffer); } if (status) { av_log(avctx, AV_LOG_ERROR, "MMAL error %d when sending input\n", (int)status); return AVERROR_UNKNOWN; } } return 0; }
void CMMALVideo::Recycle(MMAL_BUFFER_HEADER_T *buffer) { if (g_advancedSettings.CanLogComponent(LOGVIDEO)) CLog::Log(LOGDEBUG, "%s::%s %p", CLASSNAME, __func__, buffer); MMAL_STATUS_T status; mmal_buffer_header_reset(buffer); buffer->cmd = 0; if (g_advancedSettings.CanLogComponent(LOGVIDEO)) CLog::Log(LOGDEBUG, "%s::%s Send buffer %p from pool to decoder output port %p dts_queue(%d) ready_queue(%d) busy_queue(%d)", CLASSNAME, __func__, buffer, m_dec_output, m_dts_queue.size(), m_output_ready.size(), m_output_busy); status = mmal_port_send_buffer(m_dec_output, buffer); if (status != MMAL_SUCCESS) { CLog::Log(LOGERROR, "%s::%s - Failed send buffer to decoder output port (status=0%x %s)", CLASSNAME, __func__, status, mmal_status_to_string(status)); return; } }
static picture_t *decode(decoder_t *dec, block_t **pblock) { decoder_sys_t *sys = dec->p_sys; block_t *block; MMAL_BUFFER_HEADER_T *buffer; bool need_flush = false; uint32_t len; uint32_t flags = 0; MMAL_STATUS_T status; picture_t *ret = NULL; /* * Configure output port if necessary */ if (sys->output_format) { if (change_output_format(dec) < 0) msg_Err(dec, "Failed to change output port format"); } if (!pblock) goto out; block = *pblock; /* * Check whether full flush is required */ if (block && block->i_flags & BLOCK_FLAG_DISCONTINUITY) { flush_decoder(dec); block_Release(*pblock); return NULL; } /* * Send output buffers */ if (atomic_load(&sys->started)) { buffer = mmal_queue_get(sys->decoded_pictures); if (buffer) { ret = (picture_t *)buffer->user_data; ret->date = buffer->pts; ret->b_progressive = sys->b_progressive; ret->b_top_field_first = sys->b_top_field_first; if (sys->output_pool) { buffer->data = NULL; mmal_buffer_header_reset(buffer); mmal_buffer_header_release(buffer); } } fill_output_port(dec); } if (ret) goto out; /* * Process input */ if (!block) goto out; *pblock = NULL; if (block->i_flags & BLOCK_FLAG_CORRUPTED) flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED; vlc_mutex_lock(&sys->mutex); while (block->i_buffer > 0) { buffer = mmal_queue_timedwait(sys->input_pool->queue, 2); if (!buffer) { msg_Err(dec, "Failed to retrieve buffer header for input data"); need_flush = true; break; } mmal_buffer_header_reset(buffer); buffer->cmd = 0; buffer->pts = block->i_pts != 0 ? block->i_pts : block->i_dts; buffer->dts = block->i_dts; buffer->alloc_size = sys->input->buffer_size; len = block->i_buffer; if (len > buffer->alloc_size) len = buffer->alloc_size; buffer->data = block->p_buffer; block->p_buffer += len; block->i_buffer -= len; buffer->length = len; if (block->i_buffer == 0) buffer->user_data = block; buffer->flags = flags; status = mmal_port_send_buffer(sys->input, buffer); if (status != MMAL_SUCCESS) { msg_Err(dec, "Failed to send buffer to input port (status=%"PRIx32" %s)", status, mmal_status_to_string(status)); break; } atomic_fetch_add(&sys->input_in_transit, 1); } vlc_mutex_unlock(&sys->mutex); out: if (need_flush) flush_decoder(dec); return ret; }
static int send_output_buffer(decoder_t *dec) { decoder_sys_t *sys = dec->p_sys; MMAL_BUFFER_HEADER_T *buffer; picture_sys_t *p_sys; picture_t *picture; MMAL_STATUS_T status; unsigned buffer_size = 0; int ret = 0; if (!sys->output->is_enabled) return VLC_EGENERIC; /* If local output pool is allocated, use it - this is only the case for * non-opaque modes */ if (sys->output_pool) { buffer = mmal_queue_get(sys->output_pool->queue); if (!buffer) { msg_Warn(dec, "Failed to get new buffer"); return VLC_EGENERIC; } } picture = decoder_NewPicture(dec); if (!picture) { msg_Warn(dec, "Failed to get new picture"); ret = -1; goto err; } p_sys = picture->p_sys; for (int i = 0; i < picture->i_planes; i++) buffer_size += picture->p[i].i_lines * picture->p[i].i_pitch; if (sys->output_pool) { mmal_buffer_header_reset(buffer); buffer->user_data = picture; buffer->alloc_size = sys->output->buffer_size; if (buffer_size < sys->output->buffer_size) { msg_Err(dec, "Retrieved picture with too small data block (%d < %d)", buffer_size, sys->output->buffer_size); ret = VLC_EGENERIC; goto err; } if (!sys->opaque) buffer->data = picture->p[0].p_pixels; } else { buffer = p_sys->buffer; if (!buffer) { msg_Warn(dec, "Picture has no buffer attached"); picture_Release(picture); return VLC_EGENERIC; } buffer->data = p_sys->buffer->data; } buffer->cmd = 0; status = mmal_port_send_buffer(sys->output, buffer); if (status != MMAL_SUCCESS) { msg_Err(dec, "Failed to send buffer to output port (status=%"PRIx32" %s)", status, mmal_status_to_string(status)); ret = -1; goto err; } atomic_fetch_add(&sys->output_in_transit, 1); return ret; err: if (picture) picture_Release(picture); if (sys->output_pool && buffer) { buffer->data = NULL; mmal_buffer_header_release(buffer); } return ret; }
int CMMALVideo::Decode(uint8_t* pData, int iSize, double dts, double pts) { //if (g_advancedSettings.CanLogComponent(LOGVIDEO)) // CLog::Log(LOGDEBUG, "%s::%s - %-8p %-6d dts:%.3f pts:%.3f dts_queue(%d) ready_queue(%d) busy_queue(%d)", // CLASSNAME, __func__, pData, iSize, dts == DVD_NOPTS_VALUE ? 0.0 : dts*1e-6, pts == DVD_NOPTS_VALUE ? 0.0 : pts*1e-6, m_dts_queue.size(), m_output_ready.size(), m_output_busy); unsigned int demuxer_bytes = 0; uint8_t *demuxer_content = NULL; MMAL_BUFFER_HEADER_T *buffer; MMAL_STATUS_T status; while (buffer = mmal_queue_get(m_dec_output_pool->queue), buffer) Recycle(buffer); // we need to queue then de-queue the demux packet, seems silly but // mmal might not have an input buffer available when we are called // and we must store the demuxer packet and try again later. // try to send any/all demux packets to mmal decoder. unsigned space = mmal_queue_length(m_dec_input_pool->queue) * m_dec_input->buffer_size; if (pData && m_demux_queue.empty() && space >= (unsigned int)iSize) { demuxer_bytes = iSize; demuxer_content = pData; } else if (pData && iSize) { mmal_demux_packet demux_packet; demux_packet.dts = dts; demux_packet.pts = pts; demux_packet.size = iSize; demux_packet.buff = new uint8_t[iSize]; memcpy(demux_packet.buff, pData, iSize); m_demux_queue_length += demux_packet.size; m_demux_queue.push(demux_packet); } uint8_t *buffer_to_free = NULL; while (1) { while (buffer = mmal_queue_get(m_dec_output_pool->queue), buffer) Recycle(buffer); space = mmal_queue_length(m_dec_input_pool->queue) * m_dec_input->buffer_size; if (!demuxer_bytes && !m_demux_queue.empty()) { mmal_demux_packet &demux_packet = m_demux_queue.front(); if (space >= (unsigned int)demux_packet.size) { // need to lock here to retrieve an input buffer and pop the queue m_demux_queue_length -= demux_packet.size; m_demux_queue.pop(); demuxer_bytes = (unsigned int)demux_packet.size; demuxer_content = demux_packet.buff; buffer_to_free = demux_packet.buff; dts = demux_packet.dts; pts = demux_packet.pts; } } if (demuxer_content) { // 500ms timeout buffer = mmal_queue_timedwait(m_dec_input_pool->queue, 500); if (!buffer) { CLog::Log(LOGERROR, "%s::%s - mmal_queue_get failed", CLASSNAME, __func__); return VC_ERROR; } mmal_buffer_header_reset(buffer); buffer->cmd = 0; if (m_startframe && pts == DVD_NOPTS_VALUE) pts = 0; buffer->pts = pts == DVD_NOPTS_VALUE ? MMAL_TIME_UNKNOWN : pts; buffer->dts = dts == DVD_NOPTS_VALUE ? MMAL_TIME_UNKNOWN : dts; buffer->length = demuxer_bytes > buffer->alloc_size ? buffer->alloc_size : demuxer_bytes; buffer->user_data = (void *)m_decode_frame_number; // set a flag so we can identify primary frames from generated frames (deinterlace) buffer->flags = MMAL_BUFFER_HEADER_FLAG_USER0; // Request decode only (maintain ref frames, but don't return a picture) if (m_drop_state) buffer->flags |= MMAL_BUFFER_HEADER_FLAG_DECODEONLY; memcpy(buffer->data, demuxer_content, buffer->length); demuxer_bytes -= buffer->length; demuxer_content += buffer->length; if (demuxer_bytes == 0) buffer->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END; if (g_advancedSettings.CanLogComponent(LOGVIDEO)) CLog::Log(LOGDEBUG, "%s::%s - %-8p %-6d/%-6d dts:%.3f pts:%.3f flags:%x dts_queue(%d) ready_queue(%d) busy_queue(%d) demux_queue(%d) space(%d)", CLASSNAME, __func__, buffer, buffer->length, demuxer_bytes, dts == DVD_NOPTS_VALUE ? 0.0 : dts*1e-6, pts == DVD_NOPTS_VALUE ? 0.0 : pts*1e-6, buffer->flags, m_dts_queue.size(), m_output_ready.size(), m_output_busy, m_demux_queue_length, mmal_queue_length(m_dec_input_pool->queue) * m_dec_input->buffer_size); assert((int)buffer->length > 0); status = mmal_port_send_buffer(m_dec_input, buffer); if (status != MMAL_SUCCESS) { CLog::Log(LOGERROR, "%s::%s Failed send buffer to decoder input port (status=%x %s)", CLASSNAME, __func__, status, mmal_status_to_string(status)); return VC_ERROR; } if (demuxer_bytes == 0) { m_decode_frame_number++; m_startframe = true; if (m_drop_state) { m_droppedPics += m_deint ? 2:1; } else { // only push if we are successful with feeding mmal pthread_mutex_lock(&m_output_mutex); m_dts_queue.push(dts); assert(m_dts_queue.size() < 5000); pthread_mutex_unlock(&m_output_mutex); } if (m_changed_count_dec != m_changed_count) { if (g_advancedSettings.CanLogComponent(LOGVIDEO)) CLog::Log(LOGDEBUG, "%s::%s format changed frame:%d(%d)", CLASSNAME, __func__, m_changed_count_dec, m_changed_count); m_changed_count_dec = m_changed_count; if (!change_dec_output_format()) { CLog::Log(LOGERROR, "%s::%s - change_dec_output_format() failed", CLASSNAME, __func__); return VC_ERROR; } } EDEINTERLACEMODE deinterlace_request = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode; EINTERLACEMETHOD interlace_method = g_renderManager.AutoInterlaceMethod(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod); bool deinterlace = m_interlace_mode != MMAL_InterlaceProgressive; if (deinterlace_request == VS_DEINTERLACEMODE_OFF) deinterlace = false; else if (deinterlace_request == VS_DEINTERLACEMODE_FORCE) deinterlace = true; if (((deinterlace && interlace_method != m_interlace_method) || !deinterlace) && m_deint) DestroyDeinterlace(); if (deinterlace && !m_deint) CreateDeinterlace(interlace_method); if (buffer_to_free) { delete [] buffer_to_free; buffer_to_free = NULL; demuxer_content = NULL; continue; } while (buffer = mmal_queue_get(m_dec_output_pool->queue), buffer) Recycle(buffer); } } if (!demuxer_bytes) break; } int ret = 0; if (!m_output_ready.empty()) { if (g_advancedSettings.CanLogComponent(LOGVIDEO)) CLog::Log(LOGDEBUG, "%s::%s - got space for output: demux_queue(%d) space(%d)", CLASSNAME, __func__, m_demux_queue_length, mmal_queue_length(m_dec_input_pool->queue) * m_dec_input->buffer_size); ret |= VC_PICTURE; } if (mmal_queue_length(m_dec_input_pool->queue) > 0 && !m_demux_queue_length) { if (g_advancedSettings.CanLogComponent(LOGVIDEO)) CLog::Log(LOGDEBUG, "%s::%s - got output picture:%d", CLASSNAME, __func__, m_output_ready.size()); ret |= VC_BUFFER; } if (!ret) { if (g_advancedSettings.CanLogComponent(LOGVIDEO)) CLog::Log(LOGDEBUG, "%s::%s - Nothing to do: dts_queue(%d) ready_queue(%d) busy_queue(%d) demux_queue(%d) space(%d)", CLASSNAME, __func__, m_dts_queue.size(), m_output_ready.size(), m_output_busy, m_demux_queue_length, mmal_queue_length(m_dec_input_pool->queue) * m_dec_input->buffer_size); Sleep(10); // otherwise we busy spin } return ret; }