// 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; }
// 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); } }