In RubyMotion, I'm using AVFoundation for screencapture in an attempt to implement this gist from the Mac Developer Library. The program should capture video from the screen and write to a .mov file.
I don't quite understand why I'm getting this error:
* -[AVCaptureMovieFileOutput startRecordingToOutputFileURL:recordingDelegate:] - no active/enabled
connections.
The simple code is:
# Setup recording pipeline
#session = AVCaptureSession.alloc.init
#session.sessionPreset = AVCaptureSessionPresetMedium
input = AVCaptureScreenInput.alloc.initWithDisplayID(KCGDirectMainDisplay)
#session.addInput(input)
movieFileOutput = AVCaptureMovieFileOutput.alloc.init
if #session.canAddOutput(movieFileOutput)
#session.addOutput(movieFileOutput)
else
Logger.error "Could not add ouput #{movieFileOutput}"
end
#session.startRunning()
# Delete exisiting file
fileManager = NSFileManager.defaultManager
path = "~/Desktop/video.mov"
if fileManager.fileExistsAtPath(path)
err = Pointer.new(:object)
unless fileManager.defaultManager.removeItemAtPath(path, error:err)
Logger.error "Can't delete existing movie"
end
end
# Start recording
movieFileOutput.startRecordingToOutputFileURL(NSURL.fileURLWithPath(path), recordingDelegate:self) # <--- Problem
What am I doing incorrect?
I used an incorrect constant for display id. This works:
input = AVCaptureScreenInput.alloc.initWithDisplayID(CGMainDisplayID())
Related
As I stated in Q-title, I am trying to utilize an existing VLC extension developed in LUA programming language.
The Lua extension can be referred from here, when I correctly place this extension in %ProgramFiles%\VideoLAN\VLC\lua\extensions path and then open any video/audio file and run it and then when I select View > Remove current file from playlist and disk option, it closes the currently playing media file & throws this error: lua info: [vlc-delete] error: File does not exist.
Not sure but I suspect this is due to Windows quotes issue when FileName and/or FilePath contains spaces in them. And also from the error, it seems that io.popen("if exist " .. file .. " (echo 1)") : read "*l" == "1" isn't reliable for correctly detecting whether file actually exists or not.
I am relatively new to Lua programming, so can anyone assist about any better methods for checking whether file exists or not that works in latest VLC versions like 3.x+(cause I am using VLC 3.0.17.4 64Bit in Windows 10/11 64Bit), or just assist fix this mentioned issue ?
Note that when the script calls fileExists method, it does takes care of quotes properly: if not fileExists("\"" .. file .. "\"") then return nil, "File does not exist" end
After hours of troubleshooting, I was able to fix the issue, which was due to the way the quoted(space-containing) file path was passed to another function and the Lua library method of os.remove not working on the passed parameter.
So I just had an idea, I am mainly using Windows OS only, then why not convert the script to rely on Windows OS core(batch/cmd) functions only and do the safe checking of whether file exists and then delete with those core Windows functions too.
And so I just did that and got the plugin to actually check whether the file exists and delete the file, if it does then delete it. Here's the working code:
-- Copy this file to %ProgramFiles%\VideoLAN\VLC\lua\extensions\ and restart VLC Media player.
function descriptor()
return {
title = "VLC Delete Media File(Windows only)";
version = "1.0";
author = "Vicky Dev";
shortdesc = "&Remove current file from playlist and disk";
description = [[
<h1>VLC Delete Media File(Windows only)</h1>"
When you're playing a file, use this to easily
delete the current file from your <b>playlist</b> and <b>disk</b> with one click.<br>
Disclaimer: The author is not responsible for damage caused by this extension.
]];
}
end
function sleep(seconds)
local t0 = os.clock()
local tOriginal = t0
while os.clock() - t0 <= seconds and os.clock() >= tOriginal do end
end
function removeItem()
local id = vlc.playlist.current()
vlc.playlist.delete(id)
vlc.playlist.gotoitem(id + 1)
vlc.deactivate()
end
function activate()
local item = vlc.input.item()
local uri = item:uri()
uri = string.gsub(uri, "^file:///", "")
uri = vlc.strings.decode_uri(uri)
path = string.gsub(uri, "/", "\\")
vlc.msg.info("[VLC Delete Media File(Windows only)] removing: "..uri.." : "..path)
removeItem()
retval, err = os.execute("if exist ".."\""..path.."\"".." #(call )")
if (type(retval) == 'number' and retval == 0) then
os.execute("del /f /a /q ".."\""..path.."\"")
end
end
function click_ok()
d:delete()
vlc.deactivate()
end
function deactivate()
vlc.deactivate()
end
function close()
deactivate()
end
function meta_changed()
end
Hope this helps anyone who wants this cool feature of deleting the media files from player itself, instead of going to the location and doing it.
I am trying to continously read a file in ruby (which is growing over time and needs to be processed in a separate process). Currently I am archiving this with the following bit of code:
r,w = IO.pipe
pid = Process.spawn('ffmpeg'+ffmpeg_args, {STDIN => r, STDERR => STDOUT})
Process.detach pid
while true do
IO.copy_stream(open(#options['filename']), w)
sleep 1
end
However - while working - I can't imagine that this is the most performant way of doing it. An alternative would be to use the following variation:
step = 1024*4
copied = 0
pid = Process.spawn('ffmpeg'+ffmpeg_args, {STDIN => r, STDERR => STDOUT})
Process.detach pid
while true do
IO.copy_stream(open(#options['filename']), w, step, copied)
copied += step
sleep 1
end
which would only continously copy parts of the file (the issue here being if the step should ever overreach the end of the file). Other approaches such a simple read-file led to ffmpeg failing when there was no new data. With this solution the frames are dropped if no new data is available (which is what I need).
Is there a better way (more performant) to archive something like that?
EDIT:
Using the method proposed by #RaVeN I am now using the following code:
open(#options['filename'], 'rb') do |stream|
stream.seek(0, IO::SEEK_END)
queue = INotify::Notifier.new
queue.watch(#options['filename'], :modify) do
w.write stream.read
end
queue.run
end
However now ffmpeg complaints about invalid data. Is there another method than read?
I've made a small CLI script in ruby to manage a small shop for a friend, but then he wanted me to make a GUI for him, so I looked around and found shoes4.
So, I went and download it, created a small test, and run:
./bin/shoes -p swt:jar ./path/to/app.rb
and left it to create the package, then I got a warning from system that I'm running low on disc space, so I went to check the jar file, and it was over 1.5GB and still not done packaging... and the code is very small and basic:
require 'yaml'
Shoes.app do
button "Add client" do
filename = ask_open_file
para File.read(filename)
clients = YAML.load_file(filename)
id = clients[clients.length - 1][0].to_i + 1
name = ask("Enter the client's full name: ")
items = ask("Enter list of items.")
patients[id] = ["ID = "+ id.to_s,"Name = "+ pname,"list of items:\n"+ items]
File.open(filename, 'w') { |f| YAML.dump(clients, f) }
alert ("Added new patient.")
end
button "Exit" do
exit()
end
end
any idea why this small app is more than 1.5GB?? or did I try to package it wrong way??
The packager will include everything in the directory of your shoes script and below.
I'm capturing/creating user entered text into files from my app, attempting to temporarily store them in my Heroku tmp directory, then upload them to a cloud service such as Google Drive.
In using Tempfile I can successfully upload, but when using File.open I get the following error when attempting to upload:
ArgumentError (wrong number of arguments (1 for 0))
The error is on the call:
#client.upload_file_by_folder_id(save_path, #folder_id)
Where #client is a session with the cloud service, save_path is the location of the attached file for upload and #folder_id is the folder they should go into.
When I use Tempfile.new I am successful in doing so:
tempfile = Tempfile.new([final_filename, '.txt'], Rails.root.join('tmp','text-temp'))
tempfile.binmode
tempfile.write msgbody
tempfile.close
save_path = tempfile.path
upload_file = #client.upload_file_by_folder_id(save_path, #folder_id)
tempfile.unlink
File.open code is:
path = 'tmp/text-temp'
filename = "#{final_filename}.txt"
save_path = Rails.root.join(path, filename)
File.open(save_path, 'wb') do |file|
file.write(msgbody)
file.close
end
upload_file = #client.upload_file_by_folder_id(save_path, #folder_id)
File.delete(save_path)
Could it be that the File.path is a string, and Tempfile.path is the full path (but not as a string)? When I put out each, they look identical.
I'd like to use File as I don't want to change the filename of the existing attachments I'm uploading, whereas Tempfile appends to the filename.
Any and all assistance is greatly appreciated. Thanks!
In order for it to work using File, I needed to set the save_path to a string:
save_path.to_s
I'm trying to set up a Ruby script that reads from a named pipe in a loop, blocking until input is available in the pipe.
I have a process that periodically puts debugging events into a named pipe:
# Open the logging pipe
log = File.open("log_pipe", "w+") #'log_pipe' created in shell using mkfifo
...
# An interesting event happens
log.puts "Interesting event #4291 occurred"
log.flush
...
I then want a separate process that will read from this pipe and print events to the console as they happen. I've tried using code like this:
input = File.open("log_pipe", "r+")
while true
puts input.gets #I expect this to block and wait for input
end
# Kill loop with ctrl+c when done
I want the input.gets to block, waiting patiently until new input arrives in the fifo; but instead it immediately reads nil and loops again, scrolling off the top of the console window.
Two things I've tried:
I've opened the input fifo with both "r" and "r+"--I have the same problem either way;
I've tried to determine if my writing process is sending EOF (which I've heard will cause the read fifo to close)--AFAIK it isn't.
SOME CONTEXT:
If it helps, here's a 'big picture' view of what I'm trying to do:
I'm working on a game that runs in RGSS, a Ruby based game engine. Since it doesn't have good integrated debugging, I want to set up a real-time log as the game runs--as events happen in the game, I want messages to show up in a console window on the side. I can send events in the Ruby game code to a named pipe using code similar to the writer code above; I'm now trying to set up a separate process that will wait for events to show up in the pipe and show them on the console as they arrive. I'm not even sure I need Ruby to do this, but it was the first solution I could think of.
Note that I'm using mkfifo from cygwin, which I happened to have installed anyway; I wonder if that might be the source of my trouble.
If it helps anyone, here's exactly what I see in irb with my 'reader' process:
irb(main):001:0> input = File.open("mypipe", "r")
=> #<File:mypipe>
irb(main):002:0> x = input.gets
=> nil
irb(main):003:0> x = input.gets
=> nil
I don't expect the input.gets at 002 and 003 to return immediately--I expect them to block.
I found a solution that avoids using Cygwin's unreliable named pipe implementation entirely. Windows has its own named pipe facility, and there is even a Ruby Gem called win32-pipe that uses it.
Unfortunately, there appears to be no way to use Ruby Gems in an RGSS script; but by dissecting the win32-pipe gem, I was able to incorporate the same idea into an RGSS game. This code is the bare minimum needed to log game events in real time to a back channel, but it can be very useful for deep debugging.
I added a new script page right before 'Main' and added this:
module PipeLogger
# -- Change THIS to change the name of the pipe!
PIPE_NAME = "RGSSPipe"
# Constant Defines
PIPE_DEFAULT_MODE = 0 # Pipe operation mode
PIPE_ACCESS_DUPLEX = 0x00000003 # Pipe open mode
PIPE_UNLIMITED_INSTANCES = 255 # Number of concurrent instances
PIPE_BUFFER_SIZE = 1024 # Size of I/O buffer (1K)
PIPE_TIMEOUT = 5000 # Wait time for buffer (5 secs)
INVALID_HANDLE_VALUE = 0xFFFFFFFF # Retval for bad pipe handle
#-----------------------------------------------------------------------
# make_APIs
#-----------------------------------------------------------------------
def self.make_APIs
$CreateNamedPipe = Win32API.new('kernel32', 'CreateNamedPipe', 'PLLLLLLL', 'L')
$FlushFileBuffers = Win32API.new('kernel32', 'FlushFileBuffers', 'L', 'B')
$DisconnectNamedPipe = Win32API.new('kernel32', 'DisconnectNamedPipe', 'L', 'B')
$WriteFile = Win32API.new('kernel32', 'WriteFile', 'LPLPP', 'B')
$CloseHandle = Win32API.new('kernel32', 'CloseHandle', 'L', 'B')
end
#-----------------------------------------------------------------------
# setup_pipe
#-----------------------------------------------------------------------
def self.setup_pipe
make_APIs
##name = "\\\\.\\pipe\\" + PIPE_NAME
##pipe_mode = PIPE_DEFAULT_MODE
##open_mode = PIPE_ACCESS_DUPLEX
##pipe = nil
##buffer = 0.chr * PIPE_BUFFER_SIZE
##size = 0
##bytes = [0].pack('L')
##pipe = $CreateNamedPipe.call(
##name,
##open_mode,
##pipe_mode,
PIPE_UNLIMITED_INSTANCES,
PIPE_BUFFER_SIZE,
PIPE_BUFFER_SIZE,
PIPE_TIMEOUT,
0
)
if ##pipe == INVALID_HANDLE_VALUE
# If we could not open the pipe, notify the user
# and proceed quietly
print "WARNING -- Unable to create named pipe: " + PIPE_NAME
##pipe = nil
else
# Prompt the user to open the pipe
print "Please launch the RGSSMonitor.rb script"
end
end
#-----------------------------------------------------------------------
# write_to_pipe ('msg' must be a string)
#-----------------------------------------------------------------------
def self.write_to_pipe(msg)
if ##pipe
# Format data
##buffer = msg
##size = msg.size
$WriteFile.call(##pipe, ##buffer, ##buffer.size, ##bytes, 0)
end
end
#------------------------------------------------------------------------
# close_pipe
#------------------------------------------------------------------------
def self.close_pipe
if ##pipe
# Send kill message to RGSSMonitor
##buffer = "!!GAMEOVER!!"
##size = ##buffer.size
$WriteFile.call(##pipe, ##buffer, ##buffer.size, ##bytes, 0)
# Close down the pipe
$FlushFileBuffers.call(##pipe)
$DisconnectNamedPipe.call(##pipe)
$CloseHandle.call(##pipe)
##pipe = nil
end
end
end
To use this, you only need to make sure to call PipeLogger::setup_pipe before writing an event; and call PipeLogger::close_pipe before game exit. (I put the setup call at the start of 'Main', and add an ensure clause to call close_pipe.) After that, you can add a call to PipeLogger::write_to_pipe("msg") at any point in any script with any string for "msg" and write into the pipe.
I have tested this code with RPG Maker XP; it should also work with RPG Maker VX and later.
You will also need something to read FROM the pipe. There are any number of ways to do this, but a simple one is to use a standard Ruby installation, the win32-pipe Ruby Gem, and this script:
require 'rubygems'
require 'win32/pipe'
include Win32
# -- Change THIS to change the name of the pipe!
PIPE_NAME = "RGSSPipe"
Thread.new { loop { sleep 0.01 } } # Allow Ctrl+C
pipe = Pipe::Client.new(PIPE_NAME)
continue = true
while continue
msg = pipe.read.to_s
puts msg
continue = false if msg.chomp == "!!GAMEOVER!!"
end
I use Ruby 1.8.7 for Windows and the win32-pipe gem mentioned above (see here for a good reference on installing gems). Save the above as "RGSSMonitor.rb" and invoke it from the command line as ruby RGSSMonitor.rb.
CAVEATS:
The RGSS code listed above is fragile; in particular, it does not handle failure to open the named pipe. This is not usually an issue on your own development machine, but I would not recommend shipping this code.
I haven't tested it, but I suspect you'll have problems if you write a lot of things to the log without running a process to read the pipe (e.g. RGSSMonitor.rb). A Windows named pipe has a fixed size (I set it here to 1K), and by default writes will block once the pipe is filled (because no process is 'relieving the pressure' by reading from it). Unfortunately, the RPGXP engine will kill a Ruby script that has stopped running for 10 seconds. (I'm told that RPGVX has eliminated this watchdog function--in which case, the game will hang instead of abruptly terminating.)
What's probably happening is the writing process is exiting, and as there are no other writing processes, EOF is sent to the pipe which causes gets to return nil, and so your code loops continually.
To get around this you can usually just open the pipe read-write at the reader end. This works for me (on a Mac), but isn't working for you (you've tried "r" and "r+"). I'm guessing this is to due with Cygwin (POSIX says opening a FIFO read-write is undefined).
An alternative is to open the pipe twice, once read-only and once write-only. You don't use the write-only IO for anything, it's just so that there's always an active writer attached to the pipe so it doesn't get closed.
input = File.open("log_pipe", "r") # note 'r', not 'r+'
keep_open = File.open("log_pipe", "w") # ensure there's always a writer
while true
puts input.gets
end