static CMBlockBufferRef cm_block_buffer_from_gst_buffer (GstBuffer * buf, GstMapFlags flags) { OSStatus status; CMBlockBufferRef bbuf; CMBlockBufferCustomBlockSource blockSource; guint memcount, i; /* Initialize custom block source structure */ blockSource.version = kCMBlockBufferCustomBlockSourceVersion; blockSource.AllocateBlock = NULL; blockSource.FreeBlock = cm_block_buffer_freeblock; /* Determine number of memory blocks */ memcount = gst_buffer_n_memory (buf); status = CMBlockBufferCreateEmpty (NULL, memcount, 0, &bbuf); if (status != kCMBlockBufferNoErr) { GST_ERROR ("CMBlockBufferCreateEmpty returned %d", (int) status); return NULL; } /* Go over all GstMemory objects and add them to the CMBlockBuffer */ for (i = 0; i < memcount; ++i) { GstMemory *mem; GstMapInfo *info; mem = gst_buffer_get_memory (buf, i); info = g_slice_new (GstMapInfo); if (!gst_memory_map (mem, info, flags)) { GST_ERROR ("failed mapping memory"); g_slice_free (GstMapInfo, info); gst_memory_unref (mem); CFRelease (bbuf); return NULL; } blockSource.refCon = info; status = CMBlockBufferAppendMemoryBlock (bbuf, info->data, info->size, NULL, &blockSource, 0, info->size, 0); if (status != kCMBlockBufferNoErr) { GST_ERROR ("CMBlockBufferAppendMemoryBlock returned %d", (int) status); gst_memory_unmap (mem, info); g_slice_free (GstMapInfo, info); gst_memory_unref (mem); CFRelease (bbuf); return NULL; } } return bbuf; }
static void h264_dec_process(MSFilter *f) { VTH264DecCtx *ctx = (VTH264DecCtx *)f->data; mblk_t *pkt; mblk_t *nalu; mblk_t *pixbuf; MSQueue q_nalus; MSQueue q_nalus2; CMBlockBufferRef stream = NULL; CMSampleBufferRef sample = NULL; CMSampleTimingInfo timing_info; MSPicture pixbuf_desc; OSStatus status; MSList *parameter_sets = NULL; bool_t unpacking_failed; ms_queue_init(&q_nalus); ms_queue_init(&q_nalus2); // unpack RTP packet unpacking_failed = FALSE; while((pkt = ms_queue_get(f->inputs[0]))) { unpacking_failed |= (rfc3984_unpack(&ctx->unpacker, pkt, &q_nalus) != 0); } if(unpacking_failed) { ms_error("VideoToolboxDecoder: error while unpacking RTP packets"); goto fail; } // Pull out SPSs and PPSs and put them into the filter context if necessary while((nalu = ms_queue_get(&q_nalus))) { MSH264NaluType nalu_type = ms_h264_nalu_get_type(nalu); if(nalu_type == MSH264NaluTypeSPS || nalu_type == MSH264NaluTypePPS) { parameter_sets = ms_list_append(parameter_sets, nalu); } else if(ctx->format_desc || parameter_sets) { ms_queue_put(&q_nalus2, nalu); } else { ms_free(nalu); } } if(parameter_sets) { CMFormatDescriptionRef last_format = ctx->format_desc ? CFRetain(ctx->format_desc) : NULL; h264_dec_update_format_description(ctx, parameter_sets); parameter_sets = ms_list_free_with_data(parameter_sets, (void (*)(void *))freemsg); if(ctx->format_desc == NULL) goto fail; if(last_format) { CMVideoDimensions last_vsize = CMVideoFormatDescriptionGetDimensions(last_format); CMVideoDimensions vsize = CMVideoFormatDescriptionGetDimensions(ctx->format_desc); if(last_vsize.width != vsize.width || last_vsize.height != vsize.height) { ms_message("VideoToolboxDecoder: new encoded video size %dx%d -> %dx%d", (int)last_vsize.width, (int)last_vsize.height, (int)vsize.width, (int)vsize.height); ms_message("VideoToolboxDecoder: destroying decoding session"); VTDecompressionSessionInvalidate(ctx->session); CFRelease(ctx->session); ctx->session = NULL; } CFRelease(last_format); } } /* Stops proccessing if no IDR has been received yet */ if(ctx->format_desc == NULL) { ms_warning("VideoToolboxDecoder: no IDR packet has been received yet"); goto fail; } /* Initializes the decoder if it has not be done yet or reconfigure it when the size of the encoded video change */ if(ctx->session == NULL) { if(!h264_dec_init_decoder(ctx)) { ms_error("VideoToolboxDecoder: failed to initialized decoder"); goto fail; } } // Pack all nalus in a VTBlockBuffer CMBlockBufferCreateEmpty(NULL, 0, kCMBlockBufferAssureMemoryNowFlag, &stream); while((nalu = ms_queue_get(&q_nalus2))) { CMBlockBufferRef nalu_block; size_t nalu_block_size = msgdsize(nalu) + H264_NALU_HEAD_SIZE; uint32_t nalu_size = htonl(msgdsize(nalu)); CMBlockBufferCreateWithMemoryBlock(NULL, NULL, nalu_block_size, NULL, NULL, 0, nalu_block_size, kCMBlockBufferAssureMemoryNowFlag, &nalu_block); CMBlockBufferReplaceDataBytes(&nalu_size, nalu_block, 0, H264_NALU_HEAD_SIZE); CMBlockBufferReplaceDataBytes(nalu->b_rptr, nalu_block, H264_NALU_HEAD_SIZE, msgdsize(nalu)); CMBlockBufferAppendBufferReference(stream, nalu_block, 0, nalu_block_size, 0); CFRelease(nalu_block); freemsg(nalu); } if(!CMBlockBufferIsEmpty(stream)) { timing_info.duration = kCMTimeInvalid; timing_info.presentationTimeStamp = CMTimeMake(f->ticker->time, 1000); timing_info.decodeTimeStamp = CMTimeMake(f->ticker->time, 1000); CMSampleBufferCreate( NULL, stream, TRUE, NULL, NULL, ctx->format_desc, 1, 1, &timing_info, 0, NULL, &sample); status = VTDecompressionSessionDecodeFrame(ctx->session, sample, 0, NULL, NULL); CFRelease(sample); if(status != noErr) { CFRelease(stream); ms_error("VideoToolboxDecoder: error while passing encoded frames to the decoder: %d", status); if(status == kVTInvalidSessionErr) { h264_dec_uninit_decoder(ctx); } goto fail; } } CFRelease(stream); goto put_frames_out; fail: ms_filter_notify_no_arg(f, MS_VIDEO_DECODER_DECODING_ERRORS); ms_filter_lock(f); if(ctx->enable_avpf) { ms_message("VideoToolboxDecoder: sending PLI"); ms_filter_notify_no_arg(f, MS_VIDEO_DECODER_SEND_PLI); } ms_filter_unlock(f); put_frames_out: // Transfer decoded frames in the output queue ms_mutex_lock(&ctx->mutex); while((pixbuf = ms_queue_get(&ctx->queue))) { ms_mutex_unlock(&ctx->mutex); ms_yuv_buf_init_from_mblk(&pixbuf_desc, pixbuf); ms_filter_lock(f); if(pixbuf_desc.w != ctx->vsize.width || pixbuf_desc.h != ctx->vsize.height) { ctx->vsize = (MSVideoSize){ pixbuf_desc.w , pixbuf_desc.h }; } ms_average_fps_update(&ctx->fps, (uint32_t)f->ticker->time); if(ctx->first_image) { ms_filter_notify_no_arg(f, MS_VIDEO_DECODER_FIRST_IMAGE_DECODED); ctx->first_image = FALSE; } ms_filter_unlock(f); ms_queue_put(f->outputs[0], pixbuf); ms_mutex_lock(&ctx->mutex); } ms_mutex_unlock(&ctx->mutex); // Cleaning ms_queue_flush(&q_nalus); ms_queue_flush(&q_nalus2); ms_queue_flush(f->inputs[0]); return; }