codevise/pageflow

View on GitHub
lib/pageflow/zencoder_video_output_definition.rb

Summary

Maintainability
C
1 day
Test Coverage
module Pageflow
  class ZencoderVideoOutputDefinition < ZencoderOutputDefinition
    cattr_accessor :skip_hls, :skip_smil

    attr_reader :video_file

    MIN_SIZE_FOR_4K = '1921x1080'.freeze
    MIN_SIZE_FOR_FULLHD = '1281x720'.freeze

    def initialize(video_file, options = {})
      super(options)
      @video_file = video_file
    end

    def input_s3_url
      @video_file.attachment_s3_url
    end

    def outputs
      [
        mp4_highdef_definitions,
        transferable(mp4_high_definition),
        transferable(mp4_medium_definition),
        transferable(mp4_low_definition),

        dash_definitions,
        hls_definitions,
        smil_definitions,

        thumbnails_definitions
      ].flatten
    end

    private

    def mp4_highdef_definitions
      return [] unless video_file.encode_highdef?
      [transferable(mp4_4k_definition), transferable(mp4_fullhd_definition)]
    end

    def mp4_4k_definition
      {
        label: '4k',
        prepare_for_segmenting: ['hls', 'dash'],
        path: video_file.mp4_4k.path,
        video_bitrate: 22000,
        decoder_bitrate_cap: 24420,
        decoder_buffer_size: 36631,
        audio_bitrate: 320,
        size: '3839x2160',
        skip: {
          min_size: MIN_SIZE_FOR_4K
        },
        public: 1,
      }
    end

    def mp4_fullhd_definition
      {
        label: 'fullhd',
        prepare_for_segmenting: ['hls', 'dash'],
        path: video_file.mp4_fullhd.path,
        video_bitrate: 6000,
        decoder_bitrate_cap: 6660,
        decoder_buffer_size: 9990,
        audio_bitrate: 192,
        size: '1919x1080',
        skip: {
          min_size: MIN_SIZE_FOR_FULLHD
        },
        public: 1,
      }
    end

    def mp4_high_definition
      {
        label: 'high',
        prepare_for_segmenting: ['hls', 'dash'],
        path: video_file.mp4_high.path,
        video_bitrate: 3072,
        decoder_bitrate_cap: 3410,
        decoder_buffer_size: 7326,
        audio_bitrate: 128,
        size: '1280x720',
        public: 1,
      }
    end

    def mp4_medium_definition
      {
        label: 'medium',
        prepare_for_segmenting: ['hls', 'dash'],
        path: video_file.mp4_medium.path,
        video_bitrate: 1536,
        decoder_bitrate_cap: 1705,
        decoder_buffer_size: 2557,
        audio_bitrate: 64,
        audio_channels: 1,
        size: '853x480',
        public: 1
      }
    end

    def mp4_low_definition
      {
        label: 'low',
        prepare_for_segmenting: ['hls', 'dash'],
        path: video_file.mp4_low.path,
        video_bitrate: 500,
        decoder_bitrate_cap: 555,
        decoder_buffer_size: 924,
        audio_bitrate: 64,
        audio_channels: 1,
        size: '640x360',
        public: 1,
        h264_profile: 'baseline'
      }
    end

    def dash_definitions
      dash_highdef_definitions +
        [
          non_transferable(dash_high_definition),
          non_transferable(dash_medium_definition),
          non_transferable(dash_low_definition),
          non_transferable(dash_playlist_definition)
        ]
    end

    def dash_highdef_definitions
      return [] unless video_file.encode_highdef?

      [
        non_transferable(dash_fullhd_definition),
        non_transferable(dash_4k_definition)
      ]
    end

    def hls_definitions
      return [] if skip_hls

      hls_highdef_definitions +
        [
          non_transferable(hls_high_definition),
          non_transferable(hls_medium_definition),
          non_transferable(hls_low_definition),
          non_transferable(hls_playlist_definition)
        ]
    end

    def hls_highdef_definitions
      return [] unless video_file.encode_highdef?

      [
        non_transferable(hls_fullhd_definition),
        non_transferable(hls_4k_definition)
      ]
    end

    def dash_low_definition
      {
        label: 'dash-low',
        source: 'low',
        copy_audio: 'true',
        copy_video: 'true',
        streaming_delivery_format: 'dash',
        streaming_delivery_profile: 'on_demand',
        path: video_file.dash_low.path,
        type: 'segmented',
        public: 1
      }
    end

    def dash_medium_definition
      {
        label: 'dash-medium',
        source: 'medium',
        copy_audio: 'true',
        copy_video: 'true',
        streaming_delivery_format: 'dash',
        streaming_delivery_profile: 'on_demand',
        path: video_file.dash_medium.path,
        type: 'segmented',
        public: 1
      }
    end

    def dash_high_definition
      {
        label: 'dash-high',
        source: 'high',
        copy_audio: 'true',
        copy_video: 'true',
        streaming_delivery_format: 'dash',
        streaming_delivery_profile: 'on_demand',
        path: video_file.dash_high.path,
        type: 'segmented',
        public: 1
      }
    end

    def dash_fullhd_definition
      {
        label: 'dash-fullhd',
        source: 'fullhd',
        copy_audio: 'true',
        copy_video: 'true',
        streaming_delivery_format: 'dash',
        streaming_delivery_profile: 'on_demand',
        path: video_file.dash_fullhd.path,
        type: 'segmented',
        public: 1
      }
    end

    def dash_4k_definition
      {
        label: 'dash-4k',
        source: '4k',
        copy_audio: 'true',
        copy_video: 'true',
        streaming_delivery_format: 'dash',
        streaming_delivery_profile: 'on_demand',
        path: video_file.dash_4k.path,
        type: 'segmented',
        public: 1
      }
    end

    def dash_playlist_definition
      {
        label: 'dash-playlist',
        streams: dash_stream_definitions,
        allow_skipped_sources: true,
        type: 'playlist',
        streaming_delivery_format: 'dash',
        path: video_file.dash_playlist.path,
        public: 1
      }
    end

    def dash_stream_definitions
      [
        {
          source: 'dash-low',
          path: video_file.dash_low.url_relative_to(video_file.dash_playlist)
        },
        {
          source: 'dash-medium',
          path: video_file.dash_medium.url_relative_to(video_file.dash_playlist)
        },
        {
          source: 'dash-high',
          path: video_file.dash_high.url_relative_to(video_file.dash_playlist)
        }
      ] + dash_highdef_stream_definitions
    end

    def dash_highdef_stream_definitions
      return [] unless video_file.encode_highdef?
      [
        {
          source: 'dash-fullhd',
          path: video_file.dash_fullhd.url_relative_to(video_file.dash_playlist)
        },
        {
          source: 'dash-4k',
          path: video_file.dash_4k.url_relative_to(video_file.dash_playlist)
        }
      ]
    end

    def hls_low_definition
      {
        label: 'hls-low',
        format: 'ts',
        source: 'low',
        copy_audio: 'true',
        copy_video: 'true',
        path: video_file.hls_low.path,
        type: 'segmented',
        public: 1
      }
    end

    def hls_medium_definition
      {
        label: 'hls-medium',
        format: 'ts',
        source: 'medium',
        copy_audio: 'true',
        copy_video: 'true',
        path: video_file.hls_medium.path,
        type: 'segmented',
        public: 1
      }
    end

    def hls_high_definition
      {
        label: 'hls-high',
        format: 'ts',
        source: 'high',
        copy_audio: 'true',
        copy_video: 'true',
        path: video_file.hls_high.path,
        type: 'segmented',
        public: 1
      }
    end

    def hls_fullhd_definition
      {
        label: 'hls-fullhd',
        format: 'ts',
        source: 'fullhd',
        copy_audio: 'true',
        copy_video: 'true',
        path: video_file.hls_fullhd.path,
        type: 'segmented',
        public: 1
      }
    end

    def hls_4k_definition
      {
        label: 'hls-4k',
        format: 'ts',
        source: '4k',
        copy_audio: 'true',
        copy_video: 'true',
        path: video_file.hls_4k.path,
        type: 'segmented',
        public: 1
      }
    end

    def hls_playlist_definition
      {
        label: 'hls-playlist',
        streams: hls_stream_definitions,
        allow_skipped_sources: true,
        type: 'playlist',
        path: video_file.hls_playlist.path,
        public: 1
      }
    end

    def hls_stream_definitions
      [
        {
          source: 'hls-medium',
          path: video_file.hls_medium.url_relative_to(video_file.hls_playlist),
          bandwidth: 1769
        },
        {
          source: 'hls-low',
          path: video_file.hls_low.url_relative_to(video_file.hls_playlist),
          bandwidth: 619
        },
        {
          source: 'hls-high',
          path: video_file.hls_high.url_relative_to(video_file.hls_playlist),
          bandwidth: 3538
        }
      ] + hls_highdef_stream_definitions
    end

    def hls_highdef_stream_definitions
      return [] unless video_file.encode_highdef?
      [
        {
          source: 'hls-fullhd',
          path: video_file.hls_fullhd.url_relative_to(video_file.hls_playlist),
          bandwidth: 8575
        },
        {
          source: 'hls-4k',
          path: video_file.hls_4k.url_relative_to(video_file.hls_playlist),
          bandwidth: 32000
        }
      ]
    end

    def smil_definitions
      return [] if skip_smil

      smil_definition = {
        type: 'playlist',
        format: 'highwinds',
        path: video_file.smil.path,
        public: true
      }

      # EXPLANATION: Zencoder always includes all conditional outputs
      # in SMIL files even if they were skipped. To prevent generating
      # SMIL files which point to non existent files, we include three
      # definitions for the same file and apply the same conditions
      # that are used to decide whether outputs are skipped.
      #
      # Using the `min_size` values as `max_size` allows cases where
      # only one of the three SMIL outputs is skipped (i.e. if the
      # resolution is exactly equal to `MIN_SIZE_FULLHD`). Still, in
      # these cases the input file is just a tiny bit larger than the
      # next lower resolution, so we do not really care if the SMIL
      # file which does not include the higher quality does not win.
      if video_file.encode_highdef?
        [
          non_transferable(smil_definition.merge(streams: smil_default_stream_definitions,
                                                 skip: {
                                                   max_size: MIN_SIZE_FOR_FULLHD
                                                 })),
          non_transferable(smil_definition.merge(streams: smil_fullhd_stream_definitions,
                                                 skip: {
                                                   min_size: MIN_SIZE_FOR_FULLHD,
                                                   max_size: MIN_SIZE_FOR_4K
                                                 })),
          non_transferable(smil_definition.merge(streams: smil_4k_stream_definitions,
                                                 skip: {
                                                   min_size: MIN_SIZE_FOR_4K
                                                 }))
        ]
      else
        non_transferable(smil_definition.merge(streams: smil_default_stream_definitions))
      end
    end

    def smil_default_stream_definitions
      [
        {
          path: video_file.mp4_medium.url(host: :hls_origin, default_protocol: 'http'),
          bandwidth: 1769
        },
        {
          path: video_file.mp4_low.url(host: :hls_origin, default_protocol: 'http'),
          bandwidth: 619
        },
        {
          path: video_file.mp4_high.url(host: :hls_origin, default_protocol: 'http'),
          bandwidth: 3538
        }
      ]
    end

    def smil_fullhd_stream_definitions
      [
        smil_default_stream_definitions,
        {
          path: video_file.mp4_fullhd.url(host: :hls_origin, default_protocol: 'http'),
          bandwidth: 8575
        }
      ].flatten
    end

    def smil_4k_stream_definitions
      [
        smil_fullhd_stream_definitions,
        {
          path: video_file.mp4_4k.url(host: :hls_origin, default_protocol: 'http'),
          bandwidth: 32000
        }
      ].flatten
    end

    def thumbnails_definitions
      if akamai_configured?
        result = [thumbnails_definition(method(:akamai_url))]
      else
        result = [thumbnails_definition(method(:s3_url))]
        result << thumbnails_definition(method(:sftp_url)) if sftp_configured?
      end
      result
    end

    def thumbnails_definition(url_helper)
      {
        thumbnails: [
          with_credentials(label: 'poster',
                           format: video_file.zencoder_poster.format,
                           number: 1,
                           start_at_first_frame: true,
                           filename: video_file.zencoder_poster.base_name_pattern,
                           base_url: url_helper.call(video_file.zencoder_poster.dir_name),
                           public: 1),
          with_credentials(label: 'thumbnail',
                           format: video_file.zencoder_thumbnail.format,
                           number: 1,
                           filename: video_file.zencoder_thumbnail.base_name_pattern,
                           base_url: url_helper.call(video_file.zencoder_thumbnail.dir_name),
                           public: 1)
        ]
      }
    end
  end
end