Class: SonyCameraRemoteAPI::Camera

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Logging, SSDP, Utils
Defined in:
lib/sony_camera_remote_api.rb

Overview

Top-level class providing wrapper methods of Sony Camera Remote APIs.

Constant Summary collapse

CONT_SHOOT_SAVING_TIME =

Timeout for saving images captured by continuous shooting.

25
TRACKING_FOCUS_TIMEOUT =

Timeout for focusing by tracking focus.

4
TRANSFER_SIZE_LIST =

Predefined transfer sizes

%w(original large small thumbnail).freeze

Constants included from SSDP

SSDP::SSDP_RETRY_INTERVAL, SSDP::SSDP_SEARCH_RETRY, SSDP::SSDP_SEARCH_TARGET

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Utils

generate_sequencial_filenames, get_next_file_number, partial_and_unique_match, print_array_in_columns

Methods included from SSDP

#parse_device_description, #ssdp_search

Methods included from Logging

configure_logger_for, #log, log_file, logger_for, #set_level, #set_output

Constructor Details

#initialize(shelf = nil, reconnect_by: nil, log_file: $stdout, log_level: Logger::INFO, finalize: false) ⇒ Camera

Note:

It is good idea to save endpoint URLs by each cameras somewhere to omit SSDP search.

Creates a new Camera object.

Parameters:

  • shelf (Shelf) (defaults to: nil)

    Shelf class object that is used for connection.

  • reconnect_by (Proc) (defaults to: nil)

    Hook method to reconnect to the camera, which is called when Wi-Fi is disconnected. Not necessary if shelf is given.

  • log_file (String, IO, Array<String, IO>) (defaults to: $stdout)

    File name or stream to output log.

  • finalize (Boolean) (defaults to: false)

    If true, stopRecMode API is called in the destructor. As far as I know, we have no problem even if we never call stopRecMode.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/sony_camera_remote_api.rb', line 48

def initialize(shelf = nil, reconnect_by: nil, log_file: $stdout, log_level: Logger::INFO, finalize: false)
  set_output log_file
  set_level  log_level
  if shelf
    @endpoints = shelf.ep || ssdp_search
    shelf.set_ep @endpoints
    @reconnect_by = shelf.method(:reconnect)
  else
    @endpoints = ssdp_search
  end
  @reconnect_by = reconnect_by if reconnect_by

  @api_manager = CameraAPIManager.new @endpoints, reconnect_by: @reconnect_by
  @http = HTTPClient.new
  @http.connect_timeout  = @http.send_timeout = @http.receive_timeout = 10
  @retrying = Retrying.new(@reconnect_by, @http).add_common_hook do
    startRecMode! timeout: 0
  end

  # Some cameras which use "Smart Remote Control" app must call this method before remote shooting.
  startRecMode! timeout: 0

  # As far as I know, we don't have to call stopRecMode method
  # It may be useful for power-saving because stopRecMode leads to stop liveview.
  if finalize
    ObjectSpace.define_finalizer(self, self.class.finalize(self))
  end
end

Instance Attribute Details

#endpointsObject (readonly)

Returns the value of attribute endpoints.



32
33
34
# File 'lib/sony_camera_remote_api.rb', line 32

def endpoints
  @endpoints
end

Class Method Details

.finalize(this) ⇒ Object

Destructor: this calls stopRecMode API to finish shooting.



79
80
81
82
83
84
# File 'lib/sony_camera_remote_api.rb', line 79

def self.finalize(this)
  proc do
    this.stopRecMode!
    this.log.info 'Finished remote shooting function.'
  end
end

Instance Method Details

#act_focusBoolean

Note:

You have to set shooting mode to ‘still’ before calling this method.

Do focus, which is the same as half-pressing the shutter button.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new
cam.change_function_to_shoot 'still'

# Capture forever only when succeeded to focus.
loop do
  if cam.act_focus
    cam.capture_still
  end
end

Returns:

  • (Boolean)

    true if focus succeeded, false if failed.



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/sony_camera_remote_api.rb', line 399

def act_focus
  return false unless support? :actHalfPressShutter
  cancel_focus
  actHalfPressShutter
  rsp = wait_event { |r| ['Focused', 'Failed'].include? r[35]['focusStatus'] }
  if rsp[35]['focusStatus'] =='Focused'
    log.info 'Focused.'
    true
  elsif rsp[35]['focusStatus'] =='Failed'
    log.info 'Focuse failed!'
    cancelHalfPressShutter
    wait_event { |r| r[35]['focusStatus'] == 'Not Focusing' }
    false
  end
end

#act_touch_focus(x, y) ⇒ Boolean

Note:

You have to set shooting mode to ‘still’ before calling this method.

Note:

Tracking focus and Touch focus are exclusive functions. So tracking focus is automatically disabled by calling this method.

Do touch focus, by which we can specify the focus position.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new
cam.change_function_to_shoot 'still'

th = cam.start_liveview_thread do |img, info|
  focus_frame = info.frames.find { |f| f.category == 1 }
  if focus_frame
    # Get current focus position.
    puts "  top-left     = (#{focus_frame.top_left.x}, #{focus_frame.top_left.y})"
    puts "  bottom-right = (#{focus_frame.bottom_right.x}, #{focus_frame.bottom_right.y})"
  else
    puts 'No focus frame!'
  end
end

# Do touch focus ramdonly, and capture a still if focused.
loop do
  cam.act_touch_focus rand(101), rand(101)
  if cam.focused?
    cam.capture_still
  end
  sleep 1
end

Parameters:

  • x (Fixnum)

    Percentage of X-axis position.

  • y (Fixnum)

    Percentage of Y-axis position.

Returns:

  • (Boolean)

    AFType (‘Touch’ or ‘Wide’) if focus succeeded. nil if failed.

See Also:

  • AF position parameter in API reference


448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
# File 'lib/sony_camera_remote_api.rb', line 448

def act_touch_focus(x, y)
  return false unless support? :setTouchAFPosition
  cancel_focus
  set_parameter! :TrackingFocus, 'Off'

  x = [[x, 100].min, 0].max
  y = [[y, 100].min, 0].max
  result = setTouchAFPosition([x, y]).result
  if result[1]['AFResult'] == true
    log.info "Touch focus (#{x}, #{y}) OK."
    # result[1]['AFType']
    true
  else
    log.info "Touch focus (#{x}, #{y}) failed."
    false
  end
end

#act_tracking_focus(x, y) ⇒ Boolean

Note:

You have to set shooting mode to ‘still’ before calling this method.

Do tracking focus, by which the focus position automatically track the object. The focus position is expressed by percentage to the origin of coordinates, which is upper left of liveview images.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new
cam.change_function_to_shoot 'still'

th = cam.start_liveview_thread do |img, info|
  tracking_frame = info.frames.find { |f| f.category == 5 }
  if tracking_frame
    # Get tracking focus position from the liveview frame info
    puts "  top-left     = (#{tracking_frame.top_left.x}, #{tracking_frame.top_left.y})"
    puts "  bottom-right = (#{tracking_frame.bottom_right.x}, #{tracking_frame.bottom_right.y})"
  else
    puts 'No tracking frame!'
  end
end

# Capture a still image while tracking.
loop do
  if cam.tracking?
    cam.capture_still
  else
    cam.act_tracking_focus 50, 50
  end
  sleep 1
end

Parameters:

  • x (Fixnum)

    Percentage of X-axis position.

  • y (Fixnum)

    Percentage of Y-axis position.

Returns:

  • (Boolean)

    true if focus succeeded, false if failed.



498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
# File 'lib/sony_camera_remote_api.rb', line 498

def act_tracking_focus(x, y)
  return false unless support? :TrackingFocus
  cancel_focus
  set_parameter :TrackingFocus, 'On'

  x = [[x, 100].min, 0].max
  y = [[y, 100].min, 0].max
  actTrackingFocus(['xPosition' => x, 'yPosition' => y]).result
  begin
    wait_event(timeout: TRACKING_FOCUS_TIMEOUT) { |r| r[54]['trackingFocusStatus'] == 'Tracking' }
    log.info "Tracking focus (#{x}, #{y}) OK."
    true
  rescue EventTimeoutError => e
    log.info "Tracking focus (#{x}, #{y}) Failed."
    false
  end
end

#act_zoom(absolute: nil, relative: nil) ⇒ Array<Fixnum>

Do zoom. Zoom position can be specified by relative and absolute percentage within the range of 0-100. If Both option are specified, absolute position is preceded.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new

cam.act_zoom(absolute: 0)     # zoom out to the wide-end
cam.act_zoom(absolute: 100)   # zoom in to the tele-end
cam.act_zoom(relative: -50)   # zoom out by -50 from the current position

Parameters:

  • absolute (Fixnum) (defaults to: nil)

    Absolute position of the lense. 0 is the Wide-end and 100 is the Tele-end.

  • relative (Fixnum) (defaults to: nil)

    Relative percecntage to current position of the lense.

Returns:

  • (Array<Fixnum>)

    Array of initial zoom position and current zoom position.



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/sony_camera_remote_api.rb', line 347

def act_zoom(absolute: nil, relative: nil)
  # Check arguments
  return if [relative, absolute].none?
  relative = nil if [relative, absolute].all?

  # Get current position
  initial = getEvent(false).result[2]['zoomPosition']
  unless initial.between? 0, 100
    initial = wait_event { |r| r[2]['zoomPosition'].between? 0, 100 }[2]['zoomPosition']
  end
  # Return curent position if relative is 0
  return initial if relative == 0

  # Calculate target positions
  if relative
    absolute = [[initial + relative, 100].min, 0].max
  else
    absolute = [[absolute, 100].min, 0].max
  end
  relative = absolute - initial
  current = initial

  log.debug "Zoom started: #{initial} -> #{absolute} (relative: #{relative})"

  # If absolute position is wide or tele end, use only long push zoom.
  if [0, 100].include? absolute
    current = zoom_until_end absolute
  else
    # Otherwise, use both long push and 1shot zoom by relative position
    current, rest = zoom_by_long_push current, relative
    current, _    = zoom_by_1shot current, rest
  end

  log.debug "Zoom finished: #{initial} -> #{current} (target was #{absolute})"
  [initial, current]
end

#cancel_focusvoid

This method returns an undefined value.

Cancel focus If camera has been focused.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new
cam.change_function_to_shoot 'still'

# Try to focus on upper-middle position.
if cam.act_tracking_focus(50, 10)
  puts cam.focused?
  cam.capture_still
  puts cam.focused?
end


549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
# File 'lib/sony_camera_remote_api.rb', line 549

def cancel_focus
  result = getEvent(false).result
  # Canceling tracking/touch focus should be preceded for half-press
  if result[54] && result[54]['trackingFocusStatus'] == 'Tracking'
    cancelTrackingFocus
    rsp = wait_event { |r| r[54]['trackingFocusStatus'] == 'Not Tracking' }
  end
  if result[34] && result[34]['currentSet'] == true
    cancelTouchAFPosition
    rsp = wait_event { |r| r[34]['currentSet'] == false }
  end
  if result[35] && result[35]['focusStatus'] != 'Not Focusing'
    cancelHalfPressShutter
    rsp = wait_event { |r| r[35]['focusStatus'] == 'Not Focusing' }
  end
end

#capture_still(transfer: true, filename: nil, prefix: nil, dir: nil) ⇒ String, ...

Note:

You have to set shooting mode to ‘still’ before calling this method. This method can be used in following continuous-shooting mode if supported:

  • Single : take a single picture

  • Burst : take 10 pictures at a time

  • MotionShot : take 10 pictures and render the movement into a single picture

Capture still image(s) and transfer them to local storage.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new

# Capture a single still image, and then save it as images/TEST.JPG.
cam.change_function_to_shoot 'still', 'Single'
cam.capture_still filename: 'TEST.JPG', dir: 'images'

# Capture 10 images by burst shooting and save them as 'TEST_0.jpg', ... 'TEST_9.jpg'.
cam.change_function_to_shoot 'still', 'Burst'
cam.capture_still prefix: 'TEST'

Parameters:

  • transfer (Boolean) (defaults to: true)

    Flag to transfer the postview image.

  • filename (String) (defaults to: nil)

    Name of image file to be transferred. If not given, original name is used. Only available in Single/MotionShot shooting mode.

  • prefix (String) (defaults to: nil)

    Prefix of sequential image files to be transferred. If not given, original name is used. Only available in Burst shooting mode.

  • dir (String) (defaults to: nil)

    Directory where image file is saved. If not given, current directory is used.

Returns:

  • (String, Array<String>, nil)

    Filename of the transferred image(s). If ‘transfer’ is false, returns nil.



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/sony_camera_remote_api.rb', line 140

def capture_still(transfer: true, filename: nil, prefix: nil, dir: nil)
  wait_event { |r| r[1]['cameraStatus'] == 'IDLE' }
  log.info 'Capturing...'
  postview_url = ''
  time = Benchmark.realtime do
    postview_url = actTakePicture.result[0][0]
    wait_event { |r| r[1]['cameraStatus'] == 'IDLE' }
  end

  log.debug postview_url
  log.info 'Capture finished. (%.2f sec)' % [time]

  if transfer
    case get_current!(:ContShootingMode)
      when 'Burst'
        transferred = transfer_in_burst_mode postview_url, prefix: prefix, dir: dir
      else
        filename = File.basename(URI.parse(postview_url).path) if filename.nil?
        transferred = transfer_postview(postview_url, filename, dir: dir)
    end
    transferred
  end
end

#change_function_to_shoot(mode, cont = nil) ⇒ void

TODO:

make mode argument nullable

This method returns an undefined value.

Change camera function to ‘Remote Shooting’ and then set shooting mode.

Parameters:

  • mode (String)

    Shoot mode

  • cont (String) (defaults to: nil)

    Continuous shooting mode (only available when shoot mode is ‘still’)

See Also:

  • mode parameters' in API reference
  • shooting mode parameter' in API reference


94
95
96
97
98
99
100
101
# File 'lib/sony_camera_remote_api.rb', line 94

def change_function_to_shoot(mode, cont = nil)
  # cameras that does not support CameraFunction API group has only 'Remote Shooting' function
  set_parameter! :CameraFunction, 'Remote Shooting'
  set_parameter :ShootMode, mode
  if mode == 'still' && cont
    set_parameter! :ContShootingMode, cont
  end
end

#change_function_to_transfervoid

This method returns an undefined value.

Change camera function to ‘Contents Transfer’. You should call this method before using contents-retrieving methods, which are following 4 methods:

  • get_content_list

  • get_date_list

  • transfer_contents

  • delete_contents



111
112
113
# File 'lib/sony_camera_remote_api.rb', line 111

def change_function_to_transfer
  set_parameter :CameraFunction, 'Contents Transfer'
end

#delete_contents(contents) ⇒ Object

Note:

You have to set camera function to ‘Contents Transfer’ before calling this method.

Delete content(s) of camera storage.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new
cam.change_function_to_transfer

# Delete 10 newest still contents
contents = cam.get_content_list(type: 'still', count: 10)
cam.delete_contents(contents)

Parameters:

  • contents (Array<Hash>)

    array of content hashes, which can be obtained by get_content_list



875
876
877
878
879
880
881
882
883
884
885
# File 'lib/sony_camera_remote_api.rb', line 875

def delete_contents(contents)
  contents = [contents].compact unless contents.is_a? Array
  count = contents.size
  (0..((count - 1) / 100)).each do |i|
    start = i * 100
    cnt = start + 100 < count ? 100 : count - start
    param = contents[start, cnt].map { |c| c['uri'] }
    deleteContent [{'uri' => param}]
  end
  log.info "Deleted #{contents.size} contents."
end

#focused?Boolean

Return whether the camera has focused or not.

Returns:

  • (Boolean)

    true if focused, false otherwise.

See Also:



521
522
523
524
# File 'lib/sony_camera_remote_api.rb', line 521

def focused?
  result = getEvent(false).result
  result[35] && result[35]['focusStatus'] == 'Focused'
end

#get_content_list(type: nil, date: nil, sort: 'descending', count: nil) ⇒ Array<Hash>

Note:

You have to set camera function to ‘Contents Transfer’ before calling this method.

Get a list of content information. Content information is Hash object that contains URI, file name, timestamp and other informations. You can transfer contents by calling ‘transfer_contents’ method with the content information Hash. This is basically the wrapper of getContentList API. For more information about request/response, see API reference.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new
cam.change_function_to_transfer

# Get all contents
contents = cam.get_content_list
# Get still contents created on 2016/8/1
contents = cam.get_content_list(type: 'still', date: '20160801')
# Get 3 oldest movie contents
contents = cam.get_content_list(type: ['movie_xavcs', 'movie_mp4'], sort: 'ascending', count: 3)

# Get filenames and URL of each content
contents.each do |c|
  filename = c['content']['original'][0]['fileName']
  url = c['content']['original'][0]['url']
  puts "#{filename}, #{url}"
end

# Transfer contents
cam.transfer_contents(contents)

Parameters:

  • type (String, Array<String>) (defaults to: nil)

    Same as ‘type’ request parameter of getContentList API.

  • date (Boolean) (defaults to: nil)

    Date in format of ‘YYYYMMDD’ used in date-view. If not specified, flat-view is used.

  • sort (String) (defaults to: 'descending')

    Same as ‘sort’ request parameter of getContentList API.

  • count (Fixnum) (defaults to: nil)

    Number of contents to get. Unlike the one of request parameter of getContentList API, you can specify over 100.

Returns:

  • (Array<Hash>)

    Content informations

See Also:

  • API in the API reference.


715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
# File 'lib/sony_camera_remote_api.rb', line 715

def get_content_list(type: nil, date: nil, sort: 'descending', count: nil)
  type = Array(type) if type.is_a? String

  scheme = getSchemeList.result[0][0]['scheme']
  source = getSourceList([{'scheme' => scheme}]).result[0][0]['source']

  if date
    date_found = get_date_list.find { |d| d['title'] == date }
    if date_found
      contents = get_content_list_sub date_found['uri'], type: type, view: 'date', sort: sort, count: count
    else
      log.error "Cannot find any contents at date '#{date}'!"
      return []
    end
  else
    # type option is available ONLY FOR 'date' view.
    if type.present?
      # if 'type' option is specified, call getContentList with date view for every date.
      # this is because getContentList with flat view is extremely slow as a number of contents grows.
      dates = get_date_list type: type, sort: sort
      contents = []
      if count.present?
        dates.each do |date|
          num = [date['contentCount'], count - contents.size].min
          contents += get_content_list_sub date['uri'], type: type, view: 'date', sort: sort, count: num
          break if contents.size >= count
        end
        # it is no problem that a number of contents is less than count
        contents = contents[0, count]
      else
        dates.each do |date|
          contents += get_content_list_sub date['uri'], type: type, view: 'date', sort: sort, count: date['contentCount']
        end
      end
    else
      # contents = get_content_list_sub source, view: 'flat', sort: sort, count: count
      contents = get_content_list_sub source, view: 'flat', sort: sort, count: count
    end
  end
  contents
end

#get_date_list(type: nil, sort: 'descending', date_count: nil, content_count: nil) ⇒ Array<Hash>

Note:

You have to set camera function to ‘Contents Transfer’ before calling this method.

Gets a list of dates and the number of contents of each date. This is basically the wrapper of getContentList API. For more information about request/response, see API reference.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new
cam.change_function_to_transfer

# Get all dates and content counts of the associated date.
dates = cam.get_date_list
# Get 5 newest dates that contains XAVC-S movie contents.
dates = cam.get_date_list(type: 'movie_xavcs', date_count: 5)
# Get dates until the sum of still contents of each date exceeds 100.
dates = cam.get_date_list(type: 'still', content_count: 100)

dates.each do |date|
  puts "date:  #{date['title']}"            # Get date in the format 'YYYYMMdd'
  puts "count: #{date['contentCount']}"     # Get content count
  # Get contents of each date
  contents = cam.get_content_list date: date['title']
  # Transfer contents
  cam.transfer_contents contents
end

Parameters:

  • type (String, Array<String>) (defaults to: nil)

    Same as ‘type’ request parameter of getContentList API

  • sort (String) (defaults to: 'descending')

    Same as ‘sort’ request parameter of getContentList API

  • date_count (Fixnum) (defaults to: nil)

    Number of dates to get.

  • content_count (Fixnum) (defaults to: nil)

    Number of contents to get

Returns:

  • (Array<Hash>)

    An array of dates in format of ‘YYYYMMdd’ and an array of number of contents of the associated date.



786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
# File 'lib/sony_camera_remote_api.rb', line 786

def get_date_list(type: nil, sort: 'descending', date_count: nil, content_count: nil)
  type = Array(type) if type.is_a? String

  scheme = getSchemeList.result[0][0]['scheme']
  source = getSourceList([{'scheme' => scheme}]).result[0][0]['source']

  if type.present?
    # If type is specifid, get all dates and check the count of contents type later
    dates = get_content_list_sub(source, view: 'date', sort: sort)
  else
    # If not, simply get dates by date_count
    dates = get_content_list_sub(source, view: 'date', count: date_count, sort: sort)
  end

  # Filter by type, date_count and content_count.
  filtered_dates = []
  dates.each do |d|
    cnt = getContentCount([{'uri' => d['uri'], 'type' => type, 'view' => 'date'}]).result[0]['count']
    # Exclude days of 0 contents.
    if cnt > 0
      d['contentCount'] = cnt
      filtered_dates << d
    end
    # Break if contents count exceeds.
    break if content_count and filtered_dates.map { |d| d['contentCount'] }.inject(0, :+) > content_count
    # Break if date count exceeds.
    break if date_count and filtered_dates.size > date_count
  end
  filtered_dates
end

#start_continuous_shootingvoid

Note:

You have to set shooting mode to ‘still’ and continuous shooting mode to following modes:

  • Continuous : take pictures continuously until stopped.

  • Spd Priority Cont. : take pictures continuously at a rate faster than ‘Continuous’.

This method returns an undefined value.

Start continuous shooting. To stop shooting, call stop_continuous_shooting method.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new
cam.change_function_to_shoot 'still', 'Continuous'

# Start continuous shooting and transfer all images.
cam.start_continuous_shooting
sleep 5
cam.stop_continuous_shooting(transfer: true)


180
181
182
183
184
185
# File 'lib/sony_camera_remote_api.rb', line 180

def start_continuous_shooting
  wait_event { |r| r[1]['cameraStatus'] == 'IDLE' }
  startContShooting
  wait_event { |r| r[1]['cameraStatus'] == 'StillCapturing' }
  log.info 'Started continous shooting.'
end

#start_interval_recordingvoid

Note:

You have to set shooting mode to ‘intervalstill’ before calling this method.

This method returns an undefined value.

Start interval still recording (a.k.a Timelapse). To stop recording, call stop_interval_recording method.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new
cam.change_function_to_shoot('intervalstill')

# Start interval still recording (does not transfer).
cam.start_interval_recording
sleep 5
cam.stop_interval_recording


272
273
274
275
276
277
# File 'lib/sony_camera_remote_api.rb', line 272

def start_interval_recording
  wait_event { |r| r[1]['cameraStatus'] == 'IDLE' }
  startIntervalStillRec
  wait_event { |r| r[1]['cameraStatus'] == 'IntervalRecording' }
  log.info 'Started interval still recording.'
end

#start_liveview_thread(size: nil, time: nil) {|LiveviewImage, LiveviewFrameInformation| ... } ⇒ Thread

Starts a new thread that downloads streamed liveview images. This liveview thread continues downloading unless the one of the following conditions meets: The both hook method is called called each time after a liveview image or frame is downloaded.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new
cam.change_function_to_shoot 'still', 'Single'

# Start liveview streaming
th = cam.start_liveview_thread do |img|
  filename = "liveview/#{img.sequence_number}.jpg"
  File.write filename, img.jpeg_data
  puts "wrote #{filename}."
end
th.join

Parameters:

  • size (String) (defaults to: nil)

    The liveview size.

  • time (Fixnum) (defaults to: nil)

    Time in seconds until finishing liveview streaming.

Yields:

Yield Parameters:

  • liveview (LiveviewImage)

    image of each frame.

  • liveview (LiveviewFrameInformation)

    frame information of each frame. If liveview frame information is not supported, nil is always given.

Returns:

  • (Thread)

    liveview downloading thread object



590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
# File 'lib/sony_camera_remote_api.rb', line 590

def start_liveview_thread(size: nil, time: nil)
  liveview_url, frame_info_enabled = init_liveview size: size
  log.debug "liveview URL: #{liveview_url}"

  th = Thread.new do
    thread_start = loop_end = Time.now
    count = 0
    buffer = ''
    frame_info= nil
    # Ensure to finalize if the thread is killed
    begin
      # Break from loop inside when timeout
      catch :finished do
        # For reconnection
        @retrying.reconnect_and_retry_forever do
          # Retrieve streaming data
          @http.get_content(liveview_url) do |chunk|
            loop_start = Time.now
            received_sec = loop_start - loop_end

            buffer << chunk
            log.debug "start--------------------buffer.size=#{buffer.size}, #{format("%.2f", received_sec * 1000)} ms"
            begin
              obj = LiveviewPacket.read(buffer)
            rescue EOFError => e
              # Simply read more data
            rescue IOError, BinData::ValidityError => e
              # Clear buffer and read data again
              buffer = ''
            else
              # Received an packet successfully!
              case obj.payload_type
                when 0x01
                  # When payload is jpeg data
                  log.debug "  sequence  : #{obj.sequence_number}"
                  log.debug "  data_size : #{obj.payload.payload_data_size_wo_padding}"
                  log.debug "  pad_size  : #{obj.payload.padding_size}"
                  if frame_info_enabled && frame_info.nil?
                    log.debug 'frame info is not present. skipping...'
                  else
                    block_time = Benchmark.realtime do
                      yield(LiveviewImage.new(obj), frame_info)
                    end
                    log.debug "block time     : #{format('%.2f', block_time*1000)} ms."
                  end
                  count += 1
                when 0x02
                  # When payload is liveview frame information
                  log.debug "frame count = #{obj.payload.frame_count}"
                  if obj.payload.frame_count > 0
                    obj.payload.frame_data.each do |d|
                      log.debug "  category     : #{d.category}"
                      log.debug "  status       : #{d.status}, #{d.additional_status}"
                      log.debug "  top-left     : #{d.top_left.x}, #{d.top_left.y}"
                      log.debug "  bottom-right : #{d.bottom_right.x}, #{d.bottom_right.y}"
                    end
                  end
                  # Keep until next liveview image comes.
                  frame_info = LiveviewFrameInformation.new obj
              end

              last_loop_end = loop_end
              loop_end = Time.now
              loop_elapsed = loop_end - last_loop_end
              log.debug "end----------------------#{format("%.2f", loop_elapsed * 1000)} ms, #{format("%.2f", 1 / loop_elapsed)} fps"

              # Delete the packet data from buffer
              buffer = buffer[obj.num_bytes..-1]

              # Finish if time exceeds total elapsed time
              throw :finished if time && (loop_end - thread_start > time)
            end
          end
        end
      end
    rescue StandardError => e
        log.error e.backtrace.join "\n"
    ensure
      # Comes here when liveview finished or killed by signal
      puts 'Stopping Liveview...'
      stopLiveview
      total_time = Time.now - thread_start
      log.info 'Liveview thread finished.'
      log.debug "  total time: #{format('%d', total_time)} sec"
      log.debug "  count: #{format('%d', count)} frames"
      log.debug "  rate: #{format('%.2f', count/total_time)} fps"
    end
  end
  th
end

#start_loop_recordingvoid

Note:

You have to set shooting mode to ‘looprec’ before calling this method.

This method returns an undefined value.

Start loop recording. To stop recording, call stop_loop_recording method.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new
cam.change_function_to_shoot('looprec')

# Start loop movie recording (does not transfer).
cam.start_loop_recording
sleep 5
cam.stop_loop_recording


310
311
312
313
314
315
# File 'lib/sony_camera_remote_api.rb', line 310

def start_loop_recording
  wait_event { |r| r[1]['cameraStatus'] == 'IDLE' }
  startLoopRec
  wait_event { |r| r[1]['cameraStatus'] == 'LoopRecording' }
  log.info 'Started loop recording.'
end

#start_movie_recordingvoid

Note:

You have to set shooting mode to ‘movie’ before calling this method.

This method returns an undefined value.

Start movie recording. To stop recording, call stop_movie_recording method.

Examples:

# Initialize
cam = SonyCameraRemoteAPI::Camera.new
cam.change_function_to_shoot('movie')

# Record movie and transfer it.
cam.start_movie_recording
sleep 5
cam.stop_movie_recording(transfer: true)


235
236
237
238
239
240
# File 'lib/sony_camera_remote_api.rb', line 235

def start_movie_recording
  wait_event { |r| r[1]['cameraStatus'] == 'IDLE' }
  startMovieRec
  wait_event { |r| r[1]['cameraStatus'] == 'MovieRecording' }
  log.info 'Started movie recording.'
end

#stop_continuous_shooting(transfer: false, prefix: nil, dir: nil) ⇒ Array<String>?

Note:

‘transfer’ flag is set false as default, because transfer time is prone to be much longer.

Stop continuous shooting and transfers all still images.

Parameters:

  • transfer (Boolean) (defaults to: false)

    Flag to transfer the captured images.

  • prefix (String) (defaults to: nil)

    Prefix of of sequencial image files to be transferred. If not given, original name is used.

  • dir (String) (defaults to: nil)

    Directory where image file is saved. If not given, current directory is used.

Returns:

  • (Array<String>, nil)

    List of filenames of the transferred images. If ‘transfer’ is false, returns nil.



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/sony_camera_remote_api.rb', line 194

def stop_continuous_shooting(transfer: false, prefix: nil, dir: nil)
  stopContShooting
  log.info 'Stopped continuous shooting: saving...'
  urls_result = wait_event(timeout: CONT_SHOOT_SAVING_TIME) { |r| r[40].present? }
  urls = urls_result[40]['contShootingUrl'].map { |e| e['postviewUrl'] }
  log.debug 'Got URLs.'
  wait_event { |r| r[1]['cameraStatus'] == 'IDLE' }
  log.info "Saving finished: #{urls.size} images."
  if transfer
    gen = generate_sequencial_filenames prefix, 'JPG' if prefix.present?
    transferred = []
    urls.each do |url|
      if prefix.present?
        filename = gen.next
      else
        filename = File.basename(URI.parse(url).path)
      end
      result = transfer_postview(url, filename, dir: dir)
      # If transfer failed, it is possible that Wi-Fi is disconnected,
      # that means subsequent postview images become unavailable.
      break if result.nil?
      transferred << result
    end
    transferred.compact
  end
end

#stop_interval_recording(transfer: false, prefix: nil, dir: nil) ⇒ Array<String>?

Note:

‘transfer’ flag is set false as default, because transfer time is prone to be much longer.

Stop interval still recording and transfers all still images.

Parameters:

  • transfer (Boolean) (defaults to: false)

    Flag to transfer still images

  • prefix (String) (defaults to: nil)

    Prefix of sequencial image files to be transferred. If not given, original name is used.

  • dir (String) (defaults to: nil)

    Directory where image file is saved. If not given, current directory is used.

Returns:

  • (Array<String>, nil)

    List of filenames of the transferred images. If ‘transfer’ is false, returns nil.



286
287
288
289
290
291
292
293
294
# File 'lib/sony_camera_remote_api.rb', line 286

def stop_interval_recording(transfer: false, prefix: nil, dir: nil)
  stopIntervalStillRec
  wait_event { |r| r[1]['cameraStatus'] == 'IDLE' }
  num_shots = getEvent([false]).result[58]['numberOfShots']
  log.info "Stopped interval still recording: #{num_shots} images."
  if transfer
    transfer_interval_stills num_shots, prefix: prefix, dir: dir
  end
end

#stop_loop_recording(transfer: false, filename: nil, dir: nil) ⇒ String?

Note:

‘transfer’ flag is set false as default, because transfer time is prone to be much longer.

Stop loop recording and transfers the movie file.

Parameters:

  • transfer (Boolean) (defaults to: false)

    Flag to transfer the recorded movie file

  • filename (String) (defaults to: nil)

    Name of the movie file to be transferred. If not given, original name is used.

  • dir (String) (defaults to: nil)

    Directory where image file is saved. If not given, current directory is used.

Returns:

  • (String, nil)

    Filename of the transferred movie. If ‘transfer’ is false, returns nil.



324
325
326
327
328
329
330
331
# File 'lib/sony_camera_remote_api.rb', line 324

def stop_loop_recording(transfer: false, filename: nil, dir: nil)
  stopLoopRec
  wait_event { |r| r[1]['cameraStatus'] == 'IDLE' }
  log.info 'Stopped loop recording.'
  if transfer
    transfer_recorded_movie(filename: filename, dir: dir)
  end
end

#stop_movie_recording(transfer: false, filename: nil, dir: nil) ⇒ String?

Note:

‘transfer’ flag is set false as default, because transfer time is prone to be much longer.

Stop movie recording and transfers the movie file.

Parameters:

  • transfer (Boolean) (defaults to: false)

    Flag to transfer the recorded movie file.

  • filename (String) (defaults to: nil)

    Name of the movie file to be transferred. If not given, original name is used.

  • dir (String) (defaults to: nil)

    Directory where image file is saved. If not given, current directory is used.

Returns:

  • (String, nil)

    Filename of the transferred movie. If ‘transfer’ is false, returns nil.



249
250
251
252
253
254
255
256
# File 'lib/sony_camera_remote_api.rb', line 249

def stop_movie_recording(transfer: false, filename: nil, dir: nil)
  stopMovieRec
  wait_event { |r| r[1]['cameraStatus'] == 'IDLE' }
  log.info 'Stopped movie recording.'
  if transfer
    transfer_recorded_movie(filename: filename, dir: dir)
  end
end

#tracking?Boolean

Return whether the camera is tracking an object for tracking focus.

Returns:

  • (Boolean)

    true if focused, false otherwise.

See Also:



530
531
532
533
# File 'lib/sony_camera_remote_api.rb', line 530

def tracking?
  result = getEvent(false).result
  result[54] && result[54]['trackingFocusStatus'] == 'Tracking'
end

#transfer_contents(contents, filenames = [], dir: nil, size: 'original', add_postfix: true) ⇒ Object

TODO:

If ‘contents’ is directory (date), get all contents of the directory.

Note:

You have to set camera function to ‘Contents Transfer’ before calling this method.

Transfer content(s) from the camera storage.

Parameters:

  • contents (Array<Hash>)

    Array of content information, which can be obtained by get_content_list

  • filenames (Array<String>) (defaults to: [])

    Array of filename strings

  • size (String, Array<String>) (defaults to: 'original')

    Content size. available values are ‘original’, ‘large’, ‘small’, ‘thumbnail’.

  • add_postfix (Boolean) (defaults to: true)

    If true, postfix is appended for ‘large’, ‘small’ and ‘thumbnail’ content size.

See Also:



829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
# File 'lib/sony_camera_remote_api.rb', line 829

def transfer_contents(contents, filenames=[], dir: nil, size: 'original', add_postfix: true)
  contents = [contents].compact unless contents.is_a? Array
  filenames = [filenames].compact unless filenames.is_a? Array
  size = [size].compact unless size.is_a? Array
  unless size.map { |s| TRANSFER_SIZE_LIST.include? s }.all?
    log.error "'size' argument contains invalid size name!"
    log.error "Available sizes are: #{TRANSFER_SIZE_LIST}"
    return nil
  end

  if !filenames.empty?
    if contents.size > filenames.size
      log.warn 'Size of filename list is smaller than that of contents list!'
      filenames += Array.new(contents.size - filenames.size, nil)
    elsif contents.size < filenames.size
      log.warn 'Size of filename list is bigger than that of contents list!'
    end
  end

  urls_filenames = get_content_url(contents, filenames, size, add_postfix: add_postfix)
  if urls_filenames.empty?
    log.warn 'No contents to be transferred.'
    return []
  end
  log.info "#{urls_filenames.size} contents to be transferred."
  transferred = transfer_contents_sub(urls_filenames, dir)
  if transferred.size == urls_filenames.size
    log.info 'All transfer completed.'
  else
    log.info "Some files are failed to transfer (#{transferred.size}/#{urls_filenames.size})."
  end
  transferred
end