// WouldAcceptDragMessage
bool
InsertClipsDropState::WouldAcceptDragMessage(const BMessage* dragMessage)
{
	if (dragMessage->what != MSG_DRAG_CLIP)
		return false;

	// inspect the message to retrieve the clip duration
	ServerObjectManager* library;
	if (dragMessage->FindPointer("library", (void**)&library) != B_OK)
		return false;

	if (!library || !library->ReadLock())
		return false;

	fDraggedClipDuration = 0;

	Clip* clip;
	for (int32 i = 0; dragMessage->FindPointer("clip", i,
											   (void**)&clip) == B_OK; i++) {
		if (!library->HasObject(clip))
			continue;
		fDraggedClipDuration += ClipPlaylistItem::DefaultDuration(clip->Duration());
	}

	library->ReadUnlock();

	fSnapFrames.CollectSnapFrames(fView->Playlist(), fDraggedClipDuration);
	fSnapFrames.AddSnapFrame(0, fDraggedClipDuration);
	if (fView->IsPaused())
		fSnapFrames.AddSnapFrame(fView->CurrentFrame(), fDraggedClipDuration);

	return true;
}
// _LayoutBackgroundSound
status_t
CollectingPlaylist::_LayoutBackgroundSound(const ServerObjectManager* library)
{
	// find the background sound clip, if we are supposed to have one
	Clip* soundClip = NULL;
	BString soundClipID = SoundClipID();

	if (soundClipID.Length() > 0) {
		soundClip = dynamic_cast<Clip*>(library->FindObject(
			soundClipID.String()));
		if (!soundClip) {
			print_error("CollectingPlaylist::_LayoutBackgroundSound() - "
				"didn't background sound clip: %s (ignoring)\n",
				soundClipID.String());
			return B_OK;
		}
	} else {
		// no background sound configured
		return B_OK;
	}

	float volume = Value(PROPERTY_BACKGROUND_SOUND_VOLUME, (float)1.0);

	uint64 duration = Duration();
	uint64 startFrame = 0;
	while (startFrame < duration) {
		ClipPlaylistItem* item = new (nothrow) ClipPlaylistItem(soundClip);
		if (!item) {
			print_error("CollectingPlaylist::_LayoutBackgroundSound() - "
				"no memory to create ClipPlaylistItem\n");
			return B_NO_MEMORY;
		}
	
		uint64 itemDuration = soundClip->Duration();
		uint64 maxItemDuration = duration - startFrame;

		if (startFrame == 0 && itemDuration >= maxItemDuration) {
			// one item as long as first track or longer
			// cut off
			itemDuration = maxItemDuration;
			// fade in + fade out
			PropertyAnimator* animator = item->AlphaAnimator();
			if (animator) {
				// remove all keyframes to get a clean start
				animator->MakeEmpty();
				KeyFrame* first = animator->InsertKeyFrameAt(0LL);
				KeyFrame* fadeEnd = animator->InsertKeyFrameAt(3);
				KeyFrame* fadeStart = animator->InsertKeyFrameAt(
					itemDuration - 4);
				KeyFrame* last = animator->InsertKeyFrameAt(itemDuration - 1);

				if (!first || !fadeEnd || !fadeStart || !last) {
					delete item;
					print_error("CollectingPlaylist::_LayoutBackgroundSound()"
						" - no memory to add fade keyframes\n");
					return B_NO_MEMORY;
				}

				first->SetScale(0.0);
				fadeEnd->SetScale(volume);
				fadeStart->SetScale(volume);
				last->SetScale(0.0);
			}
		} else if (startFrame == 0) {
			// first item, more to come
			// fade in
			PropertyAnimator* animator = item->AlphaAnimator();
			if (animator) {
				// remove all keyframes to get a clean start
				animator->MakeEmpty();
				KeyFrame* first = animator->InsertKeyFrameAt(0LL);
				KeyFrame* fadeEnd = animator->InsertKeyFrameAt(3);
				KeyFrame* last = animator->InsertKeyFrameAt(itemDuration - 1);

				if (!first || !fadeEnd || !last) {
					delete item;
					print_error("CollectingPlaylist::_LayoutBackgroundSound()"
						" - no memory to add fade keyframes\n");
					return B_NO_MEMORY;
				}

				first->SetScale(0.0);
				fadeEnd->SetScale(volume);
				last->SetScale(volume);
			}
		} else if (itemDuration >= maxItemDuration) {
			// last item
			// cut off
			itemDuration = maxItemDuration;
			// fade out
			PropertyAnimator* animator = item->AlphaAnimator();
			if (animator) {
				// remove all keyframes to get a clean start
				animator->MakeEmpty();
				KeyFrame* first = animator->InsertKeyFrameAt(0LL);
				KeyFrame* fadeStart = animator->InsertKeyFrameAt(
					itemDuration - 4);
				KeyFrame* last = animator->InsertKeyFrameAt(itemDuration - 1);

				if (!first || !fadeStart || !last) {
					delete item;
					print_error("CollectingPlaylist::_LayoutBackgroundSound()"
						" - no memory to add fade keyframes\n");
					return B_NO_MEMORY;
				}

				first->SetScale(volume);
				fadeStart->SetScale(volume);
				last->SetScale(0.0);
			}
		} else {
			// any remaining item
			PropertyAnimator* animator = item->AlphaAnimator();
			if (animator) {
				// remove all keyframes to get a clean start
				animator->MakeEmpty();
				KeyFrame* first = animator->InsertKeyFrameAt(0LL);

				if (!first) {
					delete item;
					print_error("CollectingPlaylist::_LayoutBackgroundSound()"
						" - no memory to add fade keyframes\n");
					return B_NO_MEMORY;
				}

				first->SetScale(volume);
			}
		}

		item->SetStartFrame(startFrame);
		item->SetDuration(itemDuration);
		item->SetTrack(1);

		if (!AddItem(item)) {
			delete item;
			print_error("CollectingPlaylist::_LayoutBackgroundSound() - "
				"no memory to add ClipPlaylistItem\n");
			return B_NO_MEMORY;
		}

		startFrame += itemDuration;
	}

	return B_OK;
}
// _CollectSequence
status_t
CollectingPlaylist::_CollectSequence(BList& collectables,
	const ServerObjectManager* library)
{
	collectables.SortItems(compare_collectables);

	// find the transition clip, if we are supposed to have one
	Clip* transitionClip = NULL;
	BString transitionClipID = TransitionClipID();

	if (transitionClipID.Length() > 0) {
		transitionClip = dynamic_cast<Clip*>(library->FindObject(
			transitionClipID.String()));
		if (!transitionClip) {
			print_error("CollectingPlaylist::_CollectSequence() - "
				"didn't find transition clip: %s (ignoring)\n",
				transitionClipID.String());
		}
	}

	BString previousName;

	int64 startFrame = 0;
	uint64 maxDuration = Value(PROPERTY_DURATION, (int64)0);
	uint64 defaultDuration = ItemDuration();

	int32 count = collectables.CountItems();
	for (int32 i = 0; i < count; i++) {
		CollectablePlaylist* collectable
			= (CollectablePlaylist*)collectables.ItemAtFast(i);

		uint64 itemDuration = collectable->PreferredDuration();
		if (itemDuration == 0)
			itemDuration = defaultDuration;
		if (maxDuration > 0 && startFrame + itemDuration > maxDuration) {
			// we would go past our maximum duration, so stop here
			break;
		}

		ClipPlaylistItem* item = new (nothrow) ClipPlaylistItem(collectable);
			// the item, if it was created, has it's own reference now
		if (!item) {
			print_error("CollectingPlaylist::_CollectSequence() - "
				"no memory to create ClipPlaylistItem\n");
			return B_NO_MEMORY;
		}

		BString name(collectable->Name());
		if (i > 0 && name != previousName && transitionClip) {
//printf("new sequence starts at %ld\n", i);
			// a new sequence starts
			ClipPlaylistItem* transitionItem
				= new (nothrow) ClipPlaylistItem(transitionClip);
			if (!transitionItem || !AddItem(transitionItem)) {
				delete transitionItem;
				print_error("CollectingPlaylist::_CollectSequence() - "
					"no memory to create/add ClipPlaylistItem (transition)\n");
				return B_NO_MEMORY;
			}

			transitionItem->SetStartFrame(startFrame);
			int64 transitionDuration = transitionClip->Duration();
			transitionItem->SetDuration(transitionDuration);
			startFrame += transitionDuration;
		}

		if (!AddItem(item)) {
			delete item;
			print_error("CollectingPlaylist::_CollectSequence() - "
				"no memory to add ClipPlaylistItem\n");
			return B_NO_MEMORY;
		}

		item->SetStartFrame(startFrame);
		item->SetDuration(itemDuration);
		item->SetTrack(0);

		startFrame += itemDuration;
		previousName = name;
	}
	return B_OK;
}
Exemple #4
0
// Get an openshot::Frame object for a specific frame number of this reader.
tr1::shared_ptr<Frame> Timeline::GetFrame(long int requested_frame) throw(ReaderClosed, OutOfBoundsFrame)
{
	// Check for open reader (or throw exception)
	if (!is_open)
		throw ReaderClosed("The Timeline is closed.  Call Open() before calling this method.", "");

	// Adjust out of bounds frame number
	if (requested_frame < 1)
		requested_frame = 1;

	// Check cache
	tr1::shared_ptr<Frame> frame = final_cache.GetFrame(requested_frame);
	if (frame) {
		// Debug output
		AppendDebugMethod("Timeline::GetFrame (Cached frame found)", "requested_frame", requested_frame, "", -1, "", -1, "", -1, "", -1, "", -1);

		// Return cached frame
		return frame;
	}
	else
	{
		// Create a scoped lock, allowing only a single thread to run the following code at one time
		const GenericScopedLock<CriticalSection> lock(getFrameCriticalSection);

		// Check cache again (due to locking)
		frame = final_cache.GetFrame(requested_frame);
		if (frame) {
			// Debug output
			AppendDebugMethod("Timeline::GetFrame (Cached frame found on 2nd look)", "requested_frame", requested_frame, "", -1, "", -1, "", -1, "", -1, "", -1);

			// Return cached frame
			return frame;
		}

		// Minimum number of frames to process (for performance reasons)
		int minimum_frames = OPEN_MP_NUM_PROCESSORS;

		// Get a list of clips that intersect with the requested section of timeline
		// This also opens the readers for intersecting clips, and marks non-intersecting clips as 'needs closing'
		vector<Clip*> nearby_clips = find_intersecting_clips(requested_frame, minimum_frames, true);

		omp_set_num_threads(OPEN_MP_NUM_PROCESSORS);
		// Allow nested OpenMP sections
		omp_set_nested(true);

		// Debug output
		AppendDebugMethod("Timeline::GetFrame", "requested_frame", requested_frame, "minimum_frames", minimum_frames, "OPEN_MP_NUM_PROCESSORS", OPEN_MP_NUM_PROCESSORS, "", -1, "", -1, "", -1);

		// GENERATE CACHE FOR CLIPS (IN FRAME # SEQUENCE)
		// Determine all clip frames, and request them in order (to keep resampled audio in sequence)
		for (long int frame_number = requested_frame; frame_number < requested_frame + minimum_frames; frame_number++)
		{
			// Calculate time of timeline frame
			float requested_time = calculate_time(frame_number, info.fps);
			// Loop through clips
			for (int clip_index = 0; clip_index < nearby_clips.size(); clip_index++)
			{
				// Get clip object from the iterator
				Clip *clip = nearby_clips[clip_index];
				bool does_clip_intersect = (clip->Position() <= requested_time && clip->Position() + clip->Duration() >= requested_time);
				if (does_clip_intersect)
				{
					// Get clip frame #
					float time_diff = (requested_time - clip->Position()) + clip->Start();
					int clip_frame_number = round(time_diff * info.fps.ToFloat()) + 1;
					// Cache clip object
					clip->GetFrame(clip_frame_number);
				}
			}
		}

		#pragma omp parallel
		{
			// Loop through all requested frames
			#pragma omp for ordered firstprivate(nearby_clips, requested_frame, minimum_frames)
			for (long int frame_number = requested_frame; frame_number < requested_frame + minimum_frames; frame_number++)
			{
				// Debug output
				AppendDebugMethod("Timeline::GetFrame (processing frame)", "frame_number", frame_number, "omp_get_thread_num()", omp_get_thread_num(), "", -1, "", -1, "", -1, "", -1);

				// Init some basic properties about this frame
				int samples_in_frame = Frame::GetSamplesPerFrame(frame_number, info.fps, info.sample_rate, info.channels);

				// Create blank frame (which will become the requested frame)
				tr1::shared_ptr<Frame> new_frame(tr1::shared_ptr<Frame>(new Frame(frame_number, info.width, info.height, "#000000", samples_in_frame, info.channels)));
				new_frame->SampleRate(info.sample_rate);
				new_frame->ChannelsLayout(info.channel_layout);

				// Debug output
				AppendDebugMethod("Timeline::GetFrame (Adding solid color)", "frame_number", frame_number, "info.width", info.width, "info.height", info.height, "", -1, "", -1, "", -1);

				// Add Background Color to 1st layer (if animated or not black)
				if ((color.red.Points.size() > 1 || color.green.Points.size() > 1 || color.blue.Points.size() > 1) ||
					(color.red.GetValue(frame_number) != 0.0 || color.green.GetValue(frame_number) != 0.0 || color.blue.GetValue(frame_number) != 0.0))
				new_frame->AddColor(info.width, info.height, color.GetColorHex(frame_number));

				// Calculate time of frame
				float requested_time = calculate_time(frame_number, info.fps);

				// Debug output
				AppendDebugMethod("Timeline::GetFrame (Loop through clips)", "frame_number", frame_number, "requested_time", requested_time, "clips.size()", clips.size(), "nearby_clips.size()", nearby_clips.size(), "", -1, "", -1);

				// Find Clips near this time
				for (int clip_index = 0; clip_index < nearby_clips.size(); clip_index++)
				{
					// Get clip object from the iterator
					Clip *clip = nearby_clips[clip_index];

					// Does clip intersect the current requested time
					bool does_clip_intersect = (clip->Position() <= requested_time && clip->Position() + clip->Duration() >= requested_time);

					// Debug output
					AppendDebugMethod("Timeline::GetFrame (Does clip intersect)", "frame_number", frame_number, "requested_time", requested_time, "clip->Position()", clip->Position(), "clip->Duration()", clip->Duration(), "does_clip_intersect", does_clip_intersect, "", -1);

					// Clip is visible
					if (does_clip_intersect)
					{
						// Determine if clip is "top" clip on this layer (only happens when multiple clips are overlapping)
						bool is_top_clip = true;
						for (int top_clip_index = 0; top_clip_index < nearby_clips.size(); top_clip_index++)
						{
							Clip *nearby_clip = nearby_clips[top_clip_index];
							if (clip->Id() != nearby_clip->Id() && clip->Layer() == nearby_clip->Layer() &&
									nearby_clip->Position() <= requested_time && nearby_clip->Position() + nearby_clip->Duration() >= requested_time &&
									nearby_clip->Position() > clip->Position()) {
								is_top_clip = false;
								break;
							}
						}

						// Determine the frame needed for this clip (based on the position on the timeline)
						float time_diff = (requested_time - clip->Position()) + clip->Start();
						int clip_frame_number = round(time_diff * info.fps.ToFloat()) + 1;

						// Debug output
						AppendDebugMethod("Timeline::GetFrame (Calculate clip's frame #)", "time_diff", time_diff, "requested_time", requested_time, "clip->Position()", clip->Position(), "clip->Start()", clip->Start(), "info.fps.ToFloat()", info.fps.ToFloat(), "clip_frame_number", clip_frame_number);

						// Add clip's frame as layer
						add_layer(new_frame, clip, clip_frame_number, frame_number, is_top_clip);

					} else
						// Debug output
						AppendDebugMethod("Timeline::GetFrame (clip does not intersect)", "frame_number", frame_number, "requested_time", requested_time, "does_clip_intersect", does_clip_intersect, "", -1, "", -1, "", -1);

				} // end clip loop

				// Debug output
				AppendDebugMethod("Timeline::GetFrame (Add frame to cache)", "frame_number", frame_number, "info.width", info.width, "info.height", info.height, "", -1, "", -1, "", -1);

				// Add final frame to cache
				final_cache.Add(frame_number, new_frame);

			} // end frame loop
		} // end parallel

		// Debug output
		AppendDebugMethod("Timeline::GetFrame (end parallel region)", "requested_frame", requested_frame, "omp_get_thread_num()", omp_get_thread_num(), "", -1, "", -1, "", -1, "", -1);

		// Return frame (or blank frame)
		return final_cache.GetFrame(requested_frame);
	}
}