예제 #1
0
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;
}
예제 #2
0
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;
}