Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: exavideo/exacore
base: ed9183ba5f
...
head fork: exavideo/exacore
compare: cde9a92dfe
  • 8 commits
  • 11 files changed
  • 0 commit comments
  • 1 contributor
View
5 drivers/decklink.cpp
@@ -741,6 +741,9 @@ class DeckLinkInputAdapter : public InputAdapter,
delete out;
}
}
+ } else {
+ fprintf(stderr, "DeckLink warning: frame with no video\n");
+ out_pipe.put(NULL);
}
/* Process audio, if available. */
@@ -758,6 +761,8 @@ class DeckLinkInputAdapter : public InputAdapter,
memcpy(audio_out->data( ), data, audio_out->size( ));
audio_pipe->put(audio_out);
+ } else if (audio_pipe != NULL) {
+ fprintf(stderr, "DeckLink warning: frame with no audio\n");
}
return S_OK;
View
30 raw_frame/audio_packet.cpp
@@ -37,20 +37,27 @@ AudioPacket::AudioPacket(unsigned int rate, unsigned int channels,
_data = (uint8_t *)malloc(_size);
memset(_data, 0, _size);
- npackets++;
- if (npackets > 1000) {
- fprintf(stderr,
- "More than 1000 AudioPackets are outstanding!\n"
- "This is a sign of a memory leak.\n"
- "Please check your code!\n"
- );
- }
+ leak_detect( );
if (_data == NULL) {
throw std::runtime_error("AudioPacket: failed to allocate data");
}
}
+void AudioPacket::leak_detect( ) {
+ //npackets++;
+ size_t current_npackets = __sync_add_and_fetch(&npackets, 1);
+
+ if (current_npackets > 1000) {
+ fprintf(stderr,
+ "More than 1000 (%zd) AudioPackets are outstanding!\n"
+ "This is a sign of a memory leak.\n"
+ "Please check your code!\n",
+ npackets
+ );
+ }
+}
+
AudioPacket::AudioPacket(void *src, size_t size) {
struct sdata *sd = (struct sdata *)src;
_rate = sd->_rate;
@@ -59,16 +66,19 @@ AudioPacket::AudioPacket(void *src, size_t size) {
_sample_size = sd->_sample_size;
_data = (uint8_t *)malloc(_size);
+ leak_detect( );
+
if (size < sizeof(struct sdata) + _size) {
throw std::runtime_error("Tried to deserialize invalid AudioPacket");
}
memcpy(_data, sd->_data, _size);
-
}
AudioPacket::~AudioPacket( ) {
- npackets--;
+ //npackets--;
+ __sync_sub_and_fetch(&npackets, 1);
+
if (_data != NULL) {
free(_data);
}
View
4 raw_frame/audio_packet.h
@@ -60,7 +60,7 @@ class AudioPacket {
unsigned int _rate;
unsigned int _channels;
size_t _sample_size;
- uint8_t data[0];
+ uint8_t _data[0];
};
uint8_t *_data;
@@ -69,6 +69,8 @@ class AudioPacket {
unsigned int _channels;
size_t _sample_size;
static size_t npackets;
+
+ void leak_detect( );
};
#endif
View
2  replay/public_html/css/global.css
@@ -7,7 +7,7 @@ body {
}
.shotView {
- height: 140px;
+ height: 170px;
width: 380px;
-moz-border-radius: 5px;
View
42 replay/public_html/js/replay.js
@@ -125,6 +125,8 @@ function makeDivForShot(shot) {
new_div.find('#forward').click(seekForwardButton);
new_div.find('#in').click(markIn);
new_div.find('#out').click(markOut);
+ new_div.find("#dlvideo").click(downloadVideo);
+ new_div.find("#dlaudio").click(downloadAudio);
new_div.find('#queue').show( );
new_div.find('#copy').hide( );
@@ -189,6 +191,22 @@ function markOut() {
updateShotData(shotDiv);
}
+function downloadVideo() {
+ var shotDiv = $(this).parent();
+ var shot = shotDiv.data('shot');
+
+ url = '/sources/'+shot.source+'/'+shot.start+'/'+shot.length+'/video/shot.mjpg';
+ window.open(url, 'Video Download');
+}
+
+function downloadAudio() {
+ var shotDiv = $(this).parent();
+ var shot = shotDiv.data('shot');
+
+ url = '/sources/'+shot.source+'/'+shot.start+'/'+shot.length+'/audio/2ch_48khz/shot.raw';
+ window.open(url, 'Audio Download');
+}
+
function seekBackButton() {
var shotDiv = $(this).parent();
doSeekBack(shotDiv);
@@ -284,6 +302,26 @@ function handleKeyboard(evt) {
}
}
+function loadFiles( ) {
+ loadJson('/files.json', function(files) {
+ $.each(files, function(i, x) {
+ $('#filepicker').append('<option value="'+x+'">'+x+'</option>');
+ });
+ });
+}
+
+function rolloutFile( ) {
+ var file = $('#filepicker').val( );
+
+ if (file.length > 0) {
+ putJson({filename : file }, '/ffmpeg_rollout.json', function(){});
+ }
+}
+
+function resumeEncode( ) {
+ putJson('/resume_encode.json', { });
+}
+
$(function() {
//loadJson('/shots.json', populateShots);
//refreshTimeoutLoop();
@@ -309,5 +347,9 @@ $(function() {
$('#help').dialog({ autoOpen: false });
$(document).keydown(handleKeyboard);
+
+ loadFiles( );
+ $("#rollout_file").click(rolloutFile);
+ $("#resume_encode").click(resumeEncode);
});
View
12 replay/public_html/replay_ui.html
@@ -37,8 +37,13 @@
</div>
<div class="shotList">
- <p>Saved Shots</p>
- <div id="savedShots" class="shotScroll">
+ <p>Rollout Files</p>
+ <div>
+ <select size="10" id="filepicker"></select>
+ </div>
+ <div>
+ <button id="rollout_file">Rollout</button>
+ <button id="resume_encode">Resume Encode</button>
</div>
</div>
@@ -77,6 +82,9 @@
<button id="forward">&gt;&gt;</button>
<button id="out">Out</button>
+ <!--<br style="clear: both" />-->
+ <button id="dlvideo">Video</button>
+ <button id="dlaudio">Audio</button>
<br style="clear: both" />
</div>
View
11 replay/replay.i
@@ -1,3 +1,13 @@
+/* catch out-of-bounds replay buffer accesses, repackage as Ruby exception */
+%exception {
+ try {
+ $action
+ } catch (const ReplayFrameNotFoundException &) {
+ static VALUE myerror = rb_define_class("ReplayFrameNotFoundError", rb_eStandardError);
+ rb_raise(myerror, "Frame not found.");
+ }
+}
+
%include "stdint.i"
%include "replay_types.i"
%include "replay_shot.i"
@@ -9,3 +19,4 @@
%include "replay_ingest.i"
%include "replay_mjpeg_ingest.i"
%include "replay_gamedata.i"
+
View
4 replay/replay_app.rb
@@ -62,6 +62,10 @@ def make_shot_at(timecode)
def monitor
@ingest.monitor
end
+
+ def buffer
+ @buffer
+ end
end
class ReplayPreview
View
3  replay/replay_frame_extractor.cpp
@@ -19,6 +19,7 @@
#include "replay_frame_extractor.h"
#include "replay_buffer.h"
+#include "audio_packet.h"
ReplayFrameExtractor::ReplayFrameExtractor( )
: dec(1920, 1080), enc(1920, 1080) {
@@ -85,7 +86,7 @@ void ReplayFrameExtractor::extract_raw_audio(const ReplayShot &shot,
if (rfd.has_audio( )) {
AudioPacket apkt(rfd.audio( ), rfd.audio_size( ));
- data.assign(apkt.data( ), apkt.size( ));
+ data.assign((char *)apkt.data( ), apkt.size( ));
}
shot.source->finish_frame_read(rfd);
View
21 replay/replay_ingest.cpp
@@ -20,6 +20,7 @@
#include "replay_ingest.h"
#include "mjpeg_codec.h"
#include <assert.h>
+#include <string.h>
ReplayIngest::ReplayIngest(InputAdapter *iadp_, ReplayBuffer *buf_,
ReplayGameData *gds) {
@@ -40,7 +41,7 @@ void ReplayIngest::debug( ) {
}
void ReplayIngest::run_thread( ) {
- RawFrame *input, *thumb;
+ RawFrame *input, *thumb, *last_input;
AudioPacket *input_audio;
ReplayRawFrame *monitor_frame;
ReplayFrameData dest;
@@ -50,6 +51,8 @@ void ReplayIngest::run_thread( ) {
iadp->start( );
+ last_input = NULL;
+
for (;;) {
/* obtain frame (and maybe audio) from input adapter */
input = iadp->output_pipe( ).get( );
@@ -59,6 +62,12 @@ void ReplayIngest::run_thread( ) {
input_audio = NULL;
}
+ /* if a frame was dropped, use the last one */
+ if (input == NULL) {
+ fprintf(stderr, "ReplayIngest warning: video input returned a NULL frame\n");
+ input = last_input;
+ }
+
bool suspended;
{ MutexLock l(m);
@@ -106,8 +115,16 @@ void ReplayIngest::run_thread( ) {
monitor.put(monitor_frame);
}
+
+ if (input_audio) {
+ delete input_audio;
+ }
- delete input;
+ /* if this frame was good, keep it */
+ if (input != last_input) {
+ delete last_input;
+ last_input = input;
+ }
}
}
View
18 replay/replay_server.rb
@@ -259,7 +259,7 @@ def initialize(source, start, length)
def each
shot = Replay::ReplayShot.new
- shot.source = @source
+ shot.source = @source.buffer
length = @length
pos = @pos
@@ -273,7 +273,7 @@ def each
end
class RawAudioIterator
- def initialize(source, start, length
+ def initialize(source, start, length)
@source = source
@pos = start
@length = length
@@ -281,8 +281,8 @@ def initialize(source, start, length
def each
shot = Replay::ReplayShot.new
- shot.source = @source
- shot.length = @length
+ shot.source = @source.buffer
+ length = @length
pos = @pos
while length > 0
shot.start = pos
@@ -352,7 +352,7 @@ class ReplayServer < Patchbay
now = replay_app.source(src).make_shot_now
if tc < 0 or tc > now.start
- render :json => '', :status => 404
+ render :json => '', :error => 404
else
shot = replay_app.source(src).make_shot_at(tc)
render :jpg => shot.preview
@@ -365,14 +365,14 @@ class ReplayServer < Patchbay
now = replay_app.source(src).make_shot_now
if tc < 0 or tc > now.start
- render :json => '', :status => 404
+ render :json => '', :error => 404
else
shot = replay_app.source(src).make_shot_at(tc)
render :jpg => shot.thumbnail
end
end
- get '/sources/:id/:start/:length/video.mjpg' do
+ get '/sources/:id/:start/:length/video/:filename.mjpg' do
srcid = params[:id].to_i
source = replay_app.source(srcid)
start = params[:start].to_i
@@ -381,7 +381,7 @@ class ReplayServer < Patchbay
render :mjpg => MjpegIterator.new(source, start, length)
end
- get '/sources/:id/:start/:length/audio_2ch_48khz.raw' do
+ get '/sources/:id/:start/:length/audio/2ch_48khz/:filename.raw' do
srcid = params[:id].to_i
source = replay_app.source(srcid)
start = params[:start].to_i
@@ -392,7 +392,7 @@ class ReplayServer < Patchbay
get '/files.json' do
ROLLOUT_DIR = '/root/rollout'
- render :json => Dir.glob(ROLLOUT_DIR + '/*.{mov,mpg}').to_json
+ render :json => Dir.glob(ROLLOUT_DIR + '/*.{avi,mov,mpg}').to_json
end
put '/ffmpeg_rollout.json' do

No commit comments for this range

Something went wrong with that request. Please try again.