Merge branch 'record-and-playback-feature' of github.com:bigbluebutton/bigbluebutton into record-and-playback-feature

Conflicts:
	record-and-playback/rap/lib/recordandplayback/collectors/audio.rb
This commit is contained in:
Richard Alam 2011-04-04 09:12:08 -04:00
commit b01211d8f2
9 changed files with 377 additions and 85 deletions

View File

@ -36,7 +36,7 @@ GEM
rspec-expectations (= 2.0.0)
sinatra (1.2.1)
rack (~> 1.1)
tilt (>= 1.2.2, < 2.0)
tilt (< 2.0, >= 1.2.2)
term-ansicolor (1.0.5)
tilt (1.2.2)
vegas (0.1.8)

View File

@ -0,0 +1,31 @@
require 'lib/recordandplayback/generators/audio'
s1 = "/tmp/audio1.wav"
s2 = "/tmp/audio2.wav"
g = Generator::Audio.new
g.generate_silence(20, s1, 16000)
g.generate_silence(20, s2, 16000)
d = "/home/firstuser/dev/source/bigbluebutton/record-and-playback/rap/resources/raw/1b199e88-7df7-4842-a5f1-0e84b781c5c8/audio"
f1 = "#{d}/1b199e88-7df7-4842-a5f1-0e84b781c5c8-20110202-041247.wav"
f2 = "#{d}/1b199e88-7df7-4842-a5f1-0e84b781c5c8-20110202-041415.wav"
files = [f1, s1, f2, s2]
outf = "/tmp/final.wav"
g.concatenate_audio_files(files, outf)
wav_file = outf
ogg_file = "/tmp/final.ogg"
g.wav_to_ogg(wav_file, {:artist => "Budka Suflera",
:album => "Za Ostatni Grosz",
:track => 1,
:title => "Za Ostatni Grosz",
:date => "1981-05-01",
:composer => "composer=Romuald Lipko, Marek Dutkiewicz"},
ogg_file)

View File

@ -3,11 +3,19 @@ require 'fileutils'
module Collector
class NoSuchDirectoryException < RuntimeError
end
class NoAudioFileException < RuntimeError
end
class NoVideoFileException < RuntimeError
end
class NoPresentationException < RuntimeError
end
class NoDeskshareException < RuntimeError
end
class Audio
def location_exist?(location)
FileTest.directory?(location)
@ -17,7 +25,8 @@ module Collector
Dir.glob("#{location}/#{meeting_id}*.wav").empty?
end
# Copies the audio recordings specified by meeting_id
# from a directory to a target directory.
def collect_audio(meeting_id, from_dir, to_dir)
if not location_exist?(from_dir)
raise NoSuchDirectoryException, "Directory not found #{from_dir}"
@ -47,7 +56,6 @@ module Collector
Dir.glob("#{location}/*.flv").empty?
end
def collect_video(meeting_id, from_dir, to_dir)
if not location_exist?(from_dir)
raise NoSuchDirectoryException, "Directory not found #{from_dir}"
@ -67,17 +75,44 @@ module Collector
end
end
class Presentation
def location_exist?(location)
FileTest.directory?(location)
end
def presentation_present?(meeting_id, location)
Dir.glob("#{location}").empty?
end
def collect_presentation(meeting_id, from_dir, to_dir)
if not location_exist?(from_dir)
raise NoSuchDirectoryException, "Directory not found #{from_dir}"
end
if not location_exist?(to_dir)
raise NoSuchDirectoryException, "Directory not found #{to_dir}"
end
if (presentation_present?(meeting_id, from_dir))
raise NoPresentationException, "No video recording for #{meeting_id} in #{from_dir}"
end
Dir.glob("#{from_dir}/*.flv").each { |file|
FileUtils.cp(file, to_dir)
}
end
end
class Deskshare
def location_exist?(location)
FileTest.directory?(location)
end
def video_present?(meeting_id, location)
Dir.glob("#{location}/*.flv").empty?
def deskshare_present?(meeting_id, location)
Dir.glob("#{location}").empty?
end
def collect_video(meeting_id, from_dir, to_dir)
def collect_deskshare(meeting_id, from_dir, to_dir)
if not location_exist?(from_dir)
raise NoSuchDirectoryException, "Directory not found #{from_dir}"
end
@ -86,8 +121,8 @@ module Collector
raise NoSuchDirectoryException, "Directory not found #{to_dir}"
end
if (video_present?(meeting_id, from_dir))
raise NoVideoFileException, "No video recording for #{meeting_id} in #{from_dir}"
if (deskshare_present?(meeting_id, from_dir))
raise NoDeskshareException, "No video recording for #{meeting_id} in #{from_dir}"
end
Dir.glob("#{from_dir}/*.flv").each { |file|

View File

@ -0,0 +1,95 @@
require 'fileutils'
require 'rubygems'
require 'nokogiri'
module Generator
class Audio
# Generate a silent wav file.
# Params:
# millis - length of silence in millis
# filename - name of the resulting file (absolute location)
# sampling_rate = rate of the audio
def generate_silence(millis, filename, sampling_rate)
rate_in_ms = sampling_rate / 1000
samples = millis * rate_in_ms
temp_file = filename + ".dat"
f = File.open(temp_file, "wb")
# Write the sample rate for this audio file.
f.puts('; SampleRate ' + sampling_rate.to_s + '\n')
1.upto(samples) do |sample|
f.puts((sample / rate_in_ms).to_s + "\t0\n")
end
f.close();
proc = IO.popen("sox #{temp_file} -b 16 -r 16000 -c 1 -s #{filename}", "w+")
# Wait for the process to finish before removing the temp file
Process.wait()
# Delete the temporary raw audio file
FileUtils.rm(temp_file)
end
def concatenate_audio_files(files, out_filename)
concat_cmd = 'sox '
files.each do |file|
concat_cmd += " " + file
end
concat_cmd += " " + out_filename
#puts concat_cmd
proc = IO.popen(concat_cmd, "w+")
# Wait for the process to finish
Process.wait()
end
def wav_to_ogg(wav_file, atts, ogg_file)
proc = IO.popen("oggenc -o #{ogg_file} #{wav_file}", "w+")
Process.wait()
end
end
class AudioEvents
def get_first_timestamp_of_session(events)
@doc = Nokogiri::XML(File.open(events))
@doc.xpath("events/event").first["timestamp"].to_s
end
def get_last_timestamp_of_session(events)
@doc = Nokogiri::XML(File.open(events))
@doc.xpath("events/event").last["timestamp"].to_s
end
def get_start_audio_recording_events(events)
@doc = Nokogiri::XML(File.open(events))
start_events = []
@doc.xpath("//event[@name='StartRecordingEvent']").each do |e|
start_events << {:start_event_timestamp => e["timestamp"], :bridge => e.xpath("bridge").text,
:file => e.xpath("filename").text, :start_record_timestamp => e.xpath("recordingTimestamp").text}
end
return start_events.sort {|a,b| a[:start_event_timestamp] <=> b[:start_event_timestamp]}
end
def get_stop_audio_recording_events(events)
@doc = Nokogiri::XML(File.open(events))
stop_events = []
@doc.xpath("//event[@name='StopRecordingEvent']").each do |e|
stop_events << {:stop_event_timestamp => e["timestamp"], :bridge => e.xpath("bridge").text,
:file => e.xpath("filename").text, :stop_record_timestamp => e.xpath("recordingTimestamp").text}
end
return stop_events.sort {|a,b| a[:stop_event_timestamp] <=> b[:stop_event_timestamp]}
end
def match_start_and_stop_events(start_events, stop_events)
audio_events = []
start_events.each { |saev|
stop_events.each { |soev|
if (soev[:file] == saev[:file])
puts soev[:file]
end
}
}
end
end
end

View File

@ -0,0 +1,12 @@
require 'spec_helper'
module Generator
describe Audio do
context "#success" do
it "should create a silence file" do
ag = Generator::Audio.new
ag.generate_silence(2000,"/tmp/audio.dat", 8000)
end
end
end
end

View File

@ -0,0 +1,52 @@
require 'spec_helper'
require 'fileutils'
module Collector
describe Deskshare do
context "#success" do
it "should copy deskshare recordings to archive" do
FileTest.stub(:directory?).and_return(true)
FileUtils.stub(:cp)
Dir.stub(:glob).and_return(['file1.wav', 'file2.wav'])
from_dir = 'from'
to_dir = 'to'
meeting_id = 'meeting-id'
archiver = Collector::Deskshare.new
expect { archiver.collect_deskshare( meeting_id, from_dir, to_dir ) }.to_not raise_error
end
end
context "#fail" do
it "should raise from directory not found exception" do
FileTest.stub(:directory?).and_return(false)
FileUtils.stub(:cp)
Dir.stub(:glob).and_return(['file1.wav', 'file2.wav'])
from_dir = '/from-dir-not-found'
to_dir = 'resources/archive'
meeting_id = 'meeting-id'
archiver = Collector::Deskshare.new
expect {archiver.collect_deskshare( meeting_id, from_dir, to_dir )}.to raise_error(NoSuchDirectoryException)
end
it "should raise to directory not found exception" do
from_dir = 'resources/raw/audio'
to_dir = '/to-dir-not-found'
meeting_id = 'meeting-id'
FileTest.stub(:directory?).and_return(false)
FileUtils.stub(:cp)
Dir.stub(:glob).and_return(['file1.wav', 'file2.wav'])
archiver = Collector::Deskshare.new
expect { archiver.collect_deskshare( meeting_id, from_dir, to_dir ) }.to raise_error(NoSuchDirectoryException)
end
it "should raise deskshare files not found exception" do
from_dir = 'resources/raw/audio'
to_dir = '/to-dir-not-found'
meeting_id = 'meeting-id'
FileTest.stub(:directory?).and_return(true)
FileUtils.stub(:cp)
Dir.stub(:glob).and_return([])
archiver = Collector::Deskshare.new
expect { archiver.collect_deskshare( meeting_id, from_dir, to_dir ) }.to raise_error(NoDeskshareException)
end
end
end
end

View File

@ -0,0 +1,52 @@
require 'spec_helper'
require 'fileutils'
module Collector
describe Presentation do
context "#success" do
it "should copy presentations to archive" do
FileTest.stub(:directory?).and_return(true)
FileUtils.stub(:cp)
Dir.stub(:glob).and_return(['file1.wav', 'file2.wav'])
from_dir = 'from'
to_dir = 'to'
meeting_id = 'meeting-id'
archiver = Collector::Presentation.new
expect { archiver.collect_presentation( meeting_id, from_dir, to_dir ) }.to_not raise_error
end
end
context "#fail" do
it "should raise from directory not found exception" do
FileTest.stub(:directory?).and_return(false)
FileUtils.stub(:cp)
Dir.stub(:glob).and_return(['file1.wav', 'file2.wav'])
from_dir = '/from-dir-not-found'
to_dir = 'resources/archive'
meeting_id = 'meeting-id'
archiver = Collector::Presentation.new
expect {archiver.collect_presentation( meeting_id, from_dir, to_dir )}.to raise_error(NoSuchDirectoryException)
end
it "should raise to directory not found exception" do
from_dir = 'resources/raw/audio'
to_dir = '/to-dir-not-found'
meeting_id = 'meeting-id'
FileTest.stub(:directory?).and_return(false)
FileUtils.stub(:cp)
Dir.stub(:glob).and_return(['file1.wav', 'file2.wav'])
archiver = Collector::Presentation.new
expect { archiver.collect_presentation( meeting_id, from_dir, to_dir ) }.to raise_error(NoSuchDirectoryException)
end
it "should raise presentation files not found exception" do
from_dir = 'resources/raw/audio'
to_dir = '/to-dir-not-found'
meeting_id = 'meeting-id'
FileTest.stub(:directory?).and_return(true)
FileUtils.stub(:cp)
Dir.stub(:glob).and_return([])
archiver = Collector::Presentation.new
expect { archiver.collect_presentation( meeting_id, from_dir, to_dir ) }.to raise_error(NoPresentationException)
end
end
end
end

View File

@ -1 +1,2 @@
require 'recordandplayback'
require 'recordandplayback/generators/audio.rb'

View File

@ -0,0 +1,14 @@
require 'lib/recordandplayback/generators/audio'
e = 'resources/raw/1b199e88-7df7-4842-a5f1-0e84b781c5c8/events.xml'
g = Generator::AudioEvents.new
puts g.get_first_timestamp_of_session(e)
puts g.get_last_timestamp_of_session(e)
puts "Start Events"
sd = g.get_start_audio_recording_events(e)
puts "Stop Events"
sf = g.get_stop_audio_recording_events(e)
g.match_start_and_stop_events(sd, sf)