/* -LICENSE-START- ** Copyright (c) 2009 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation covered by ** this license (the "Software") to use, reproduce, display, distribute, ** execute, and transmit the Software, and to prepare derivative works of the ** Software, and to permit third-parties to whom the Software is furnished to ** do so, all subject to the following: ** ** The copyright notices in the Software and this entire statement, including ** the above license grant, this restriction and the following disclaimer, ** must be included in all copies of the Software, in whole or in part, and ** all derivative works of the Software, unless such copies or derivative ** works are solely in the form of machine-executable object code generated by ** a source language processor. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** -LICENSE-END- */ #include "../include/DecklinkOutput.h" using namespace std; DeckLinkOutputDelegate::DeckLinkOutputDelegate(IDeckLinkDisplayMode *displayMode, IDeckLinkOutput* m_deckLinkOutput) : m_refCount(0), displayMode(displayMode) { // reference to output device deckLinkOutput = m_deckLinkOutput; // init some variables m_totalFramesScheduled = 0; m_audioChannelCount = 2; m_audioSampleRate = bmdAudioSampleRate48kHz; m_audioSampleDepth = 16; m_outputSignal = kOutputSignalDrop; m_currentFrame = NULL; // Get framerate displayMode->GetFrameRate(&frameRateDuration, &frameRateScale); m_framesPerSecond = (unsigned long)((frameRateScale + (frameRateDuration-1)) / frameRateDuration); // Allocate audio array m_audioBufferSampleLength = (unsigned long)((m_framesPerSecond * m_audioSampleRate * frameRateDuration) / frameRateScale); m_audioBuffer = valloc(m_audioBufferSampleLength * m_audioChannelCount * (m_audioSampleDepth / 8)); // Zero the buffer (interpreted as audio silence) memset(m_audioBuffer, 0x0, (m_audioBufferSampleLength * m_audioChannelCount * m_audioSampleDepth/8)); audioSamplesPerFrame = (unsigned long)((m_audioSampleRate * frameRateDuration) / frameRateScale); pthread_mutex_init(&m_mutex, NULL); } DeckLinkOutputDelegate::~DeckLinkOutputDelegate() { cout << "DESTRUCTOR!!!" << endl; pthread_mutex_destroy(&m_mutex); } /************************* DeckLink API Delegate Methods *****************************/ HRESULT DeckLinkOutputDelegate::ScheduledFrameCompleted (IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result) { //cout << "Scheduled Successfully!" << endl; // When a video frame has been released by the API, schedule another video frame to be output ScheduleNextFrame(false); return S_OK; } HRESULT DeckLinkOutputDelegate::ScheduledPlaybackHasStopped () { //cout << "PLAYBACK HAS STOPPED!!!" << endl; return S_OK; } HRESULT DeckLinkOutputDelegate::RenderAudioSamples (bool preroll) { // // Provide further audio samples to the DeckLink API until our preferred buffer waterlevel is reached // const unsigned long kAudioWaterlevel = 48000; // unsigned int bufferedSamples; // // // Try to maintain the number of audio samples buffered in the API at a specified waterlevel // if ((deckLinkOutput->GetBufferedAudioSampleFrameCount(&bufferedSamples) == S_OK) && (bufferedSamples < kAudioWaterlevel)) // { // unsigned int samplesToEndOfBuffer; // unsigned int samplesToWrite; // unsigned int samplesWritten; // // samplesToEndOfBuffer = (m_audioBufferSampleLength - m_audioBufferOffset); // samplesToWrite = (kAudioWaterlevel - bufferedSamples); // if (samplesToWrite > samplesToEndOfBuffer) // samplesToWrite = samplesToEndOfBuffer; // // if (deckLinkOutput->ScheduleAudioSamples((void*)((unsigned long)m_audioBuffer + (m_audioBufferOffset * m_audioChannelCount * m_audioSampleDepth/8)), samplesToWrite, 0, 0, &samplesWritten) == S_OK) // { // m_audioBufferOffset = ((m_audioBufferOffset + samplesWritten) % m_audioBufferSampleLength); // } // } // // // if (preroll) // { // // Start audio and video output // deckLinkOutput->StartScheduledPlayback(0, 100, 1.0); // } return S_OK; } // Schedule the next frame void DeckLinkOutputDelegate::ScheduleNextFrame(bool prerolling) { #pragma omp critical (blackmagic_output_queue) { // Get oldest frame (if any) if (final_frames.size() > 0) { // Release the current frame (if any) if (m_currentFrame) { m_currentFrame->Release(); m_currentFrame = NULL; } // Get the next frame off the queue //cout << "Queue: get next (" << final_frames.size() - 1 << " remaining)" << endl; m_currentFrame = final_frames.front(); final_frames.pop_front(); // remove this frame from the queue } else cout << "Queue: empty on writer..." << endl; // Schedule a frame to be displayed if (m_currentFrame && deckLinkOutput->ScheduleVideoFrame(m_currentFrame, (m_totalFramesScheduled * frameRateDuration), frameRateDuration, frameRateScale) != S_OK) cout << "ScheduleVideoFrame FAILED!!! " << m_totalFramesScheduled << endl; // Update the timestamp (regardless of previous frame's success) m_totalFramesScheduled += 1; } // critical } void DeckLinkOutputDelegate::WriteFrame(tr1::shared_ptr frame) { #pragma omp critical (blackmagic_output_queue) // Add raw OpenShot frame object raw_video_frames.push_back(frame); // Process frames once we have a few (to take advantage of multiple threads) if (raw_video_frames.size() >= omp_get_num_procs()) { //omp_set_num_threads(1); omp_set_nested(true); #pragma omp parallel { #pragma omp single { // Temp frame counters (to keep the frames in order) frameCount = 0; // Loop through each queued image frame while (!raw_video_frames.empty()) { // Get front frame (from the queue) tr1::shared_ptr frame = raw_video_frames.front(); raw_video_frames.pop_front(); // Create a new RGB frame object IDeckLinkMutableVideoFrame *m_rgbFrame = NULL; while (deckLinkOutput->CreateVideoFrame( frame->GetWidth(), frame->GetHeight(), frame->GetWidth() * 4, bmdFormat8BitARGB, bmdFrameFlagDefault, &m_rgbFrame) != S_OK) { // keep trying usleep(1000 * 1); } // copy of frame count unsigned long copy_frameCount(frameCount); #pragma omp task firstprivate(m_rgbFrame, frame, copy_frameCount) { // *********** CONVERT YUV source frame to RGB ************ void *frameBytes; void *audioFrameBytes; int width = frame->GetWidth(); int height = frame->GetHeight(); // Get RGB Byte array m_rgbFrame->GetBytes(&frameBytes); uint8_t *castBytes = (uint8_t *) frameBytes; // Get a list of pixels in our frame's image. Each pixel is represented by // a PixelPacket struct, which has 4 properties: .red, .blue, .green, .alpha const Magick::PixelPacket *pixel_packets = frame->GetPixels(); // loop through ImageMagic pixel structs, and put the colors in a regular array, and move the // colors around to match the Decklink order (ARGB). int numBytes = m_rgbFrame->GetRowBytes() * height; for (int packet = 0, row = 0; row < numBytes; packet++, row+=4) { // Update buffer (which is already linked to the AVFrame: pFrameRGB) castBytes[row] = 0; // alpha castBytes[row+1] = pixel_packets[packet].red >> 8; castBytes[row+2] = pixel_packets[packet].green >> 8; castBytes[row+3] = pixel_packets[packet].blue >> 8; } #pragma omp critical (blackmagic_output_queue) { //if (20 == frame->number) // frame->Display(); // Add processed frame to cache (to be recalled in order after the thread pool is done) temp_cache[copy_frameCount] = m_rgbFrame; } // Add processed RGB frame to final_frames //final_frames.push_back(m_rgbFrame); } // end task // Increment frame count frameCount++; } // end while } // omp single } // omp parallel // Add frames to final queue (in order) for (int z = 0; z < frameCount; z++) { IDeckLinkMutableVideoFrame *m_rgbFrame = temp_cache[z]; // Add to final queue final_frames.push_back(m_rgbFrame); } // Clear temp cache temp_cache.clear(); //cout << "final_frames.size(): " << final_frames.size() << ", raw_video_frames.size(): " << raw_video_frames.size() << endl; if (final_frames.size() >= m_framesPerSecond && m_totalFramesScheduled == 0) { cout << "Prerolling!" << endl; for (int x = 0; x < final_frames.size(); x++) ScheduleNextFrame(true); cout << "Starting scheduled playback!" << endl; // Start playback when enough frames have been processed deckLinkOutput->StartScheduledPlayback(0, 100, 1.0); } else { // Be sure we don't have too many extra frames while (final_frames.size() > (m_framesPerSecond + 15)) { cout << "Too many, so remove some..." << endl; // Remove oldest frame final_frames.front()->Release(); final_frames.pop_front(); } } } // if }