API-интерфейсы Transformer в Jetpack Media3 предназначены для повышения производительности и надежности редактирования мультимедиа. Transformer поддерживает ряд операций, в том числе:
- Изменение видео с помощью обрезки, масштабирования и вращения.
- Добавление эффектов, таких как наложения и фильтры
- Обработка специальных форматов, таких как HDR и замедленное видео.
- Экспорт медиа-элемента после применения изменений
На этой странице представлены некоторые ключевые варианты использования Transformer. Для получения более подробной информации вы можете обратиться к нашим полным руководствам по Media3 Transformer .
Начать
Для начала добавьте зависимость к модулям Transformer, Effect и Common Jetpack Media3:
implementation "androidx.media3:media3-transformer:1.5.0" implementation "androidx.media3:media3-effect:1.5.0" implementation "androidx.media3:media3-common:1.5.0"
Обязательно замените 1.5.0
предпочитаемой версией библиотеки. Вы можете обратиться к примечаниям к выпуску, чтобы увидеть последнюю версию.
Важные занятия
Сорт | Цель |
---|---|
Transformer | Запускайте и останавливайте преобразования, а также проверяйте обновления хода выполнения текущего преобразования. |
EditedMediaItem | Представляет элемент мультимедиа для обработки и изменения, которые необходимо к нему применить. |
Effects | Коллекция аудио и видео эффектов. |
Настройте вывод
С помощью Transformer.Builder
теперь вы можете указать каталог videoMimeType
и audioMimetype
, задав функцию без необходимости создания объекта TransformationRequest
.
Перекодирование между форматами
Следующий код показывает, как настроить объект Transformer
для вывода видео H.265/AVC и аудио AAC:
Котлин
val transformer = Transformer.Builder(context) .setVideoMimeType(MimeTypes.VIDEO_H265) .setAudioMimeType(MimeTypes.AUDIO_AAC) .build()
Ява
Transformer transformer = new Transformer.Builder(context) .setVideoMimeType(MimeTypes.VIDEO_H265) .setAudioMimeType(MimeTypes.AUDIO_AAC) .build();
Если формат входного мультимедиа уже соответствует запросу на преобразование аудио или видео, Transformer автоматически переключается на трансмуксирование , то есть копирование сжатых сэмплов из входного контейнера в выходной контейнер без изменений. Это позволяет избежать вычислительных затрат и потенциальной потери качества при декодировании и перекодировании в одном и том же формате.
Установить режим HDR
Если входной медиафайл имеет формат HDR, вы можете выбрать один из нескольких различных режимов обработки информации HDR Transformer. Вероятно, вы захотите использовать HDR_MODE_KEEP_HDR
или HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL
.
HDR_MODE_KEEP_HDR | HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL | |
---|---|---|
Описание | Сохраняйте данные HDR, то есть выходной формат HDR совпадает с входным форматом HDR. | Ввод Tonemap HDR в SDR с использованием преобразователя тонов OpenGL, что означает, что выходной формат будет в SDR. |
Поддерживать | Поддерживается на уровнях API 31+ для устройств, включающих кодировщик с возможностью FEATURE_HdrEditing . | Поддерживается на уровнях API 29+. |
Ошибки | Если не поддерживается, вместо этого пытается использовать HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL . | Если не поддерживается, выдает ExportException . |
На устройствах, которые поддерживают необходимые возможности кодирования и работают под управлением Android 13 (уровень API 33) или более поздней версии, объекты Transformer
позволяют редактировать HDR-видео. HDR_MODE_KEEP_HDR
— это режим по умолчанию при построении объекта Composition
, как показано в следующем коде:
Котлин
val composition = Composition.Builder( ImmutableList.of(videoSequence)) .setHdrMode(HDR_MODE_KEEP_HDR) .build()
Ява
Composition composition = new Composition.Builder( ImmutableList.of(videoSequence)) .setHdrMode(Composition.HDR_MODE_KEEP_HDR) .build();
Подготовьте медиа-материал
MediaItem
представляет собой аудио- или видеоэлемент в вашем приложении. EditedMediaItem
собирает MediaItem
вместе с преобразованиями, которые необходимо к нему применить.
Обрезать видео
Чтобы удалить ненужные части видео, вы можете установить собственные начальную и конечную позиции, добавив ClippingConfiguration
в MediaItem
.
Котлин
val clippingConfiguration = MediaItem.ClippingConfiguration.Builder() .setStartPositionMs(10_000) // start at 10 seconds .setEndPositionMs(20_000) // end at 20 seconds .build() val mediaItem = MediaItem.Builder() .setUri(videoUri) .setClippingConfiguration(clippingConfiguration) .build()
Ява
ClippingConfiguration clippingConfiguration = new MediaItem.ClippingConfiguration.Builder() .setStartPositionMs(10_000) // start at 10 seconds .setEndPositionMs(20_000) // end at 20 seconds .build(); MediaItem mediaItem = new MediaItem.Builder() .setUri(videoUri) .setClippingConfiguration(clippingConfiguration) .build();
Используйте встроенные эффекты
Media3 включает в себя ряд встроенных видеоэффектов для распространенных преобразований, например:
Сорт | Эффект |
---|---|
Presentation | Масштабируйте медиа-элемент по разрешению или соотношению сторон. |
ScaleAndRotateTransformation | Масштабируйте элемент мультимедиа с помощью множителя и/или вращайте элемент мультимедиа. |
Crop | Обрезать медиа-элемент до меньшего или большего размера кадра |
OverlayEffect | Добавьте наложение текста или изображения поверх элемента мультимедиа. |
Для звуковых эффектов вы можете добавить последовательность экземпляров AudioProcessor
, которые будут преобразовывать необработанные аудиоданные (PCM). Например, вы можете использовать ChannelMixingAudioProcessor
для микширования и масштабирования аудиоканалов.
Чтобы использовать эти эффекты, создайте экземпляр эффекта или аудиопроцессора, создайте экземпляр Effects
с аудио- и видеоэффектами, которые вы хотите применить к элементу мультимедиа, а затем добавьте объект Effects
в EditedMediaItem
.
Котлин
val channelMixingProcessor = ChannelMixingAudioProcessor() val rotateEffect = ScaleAndRotateTransformation.Builder().setRotationDegrees(60f).build() val cropEffect = Crop(-0.5f, 0.5f, -0.5f, 0.5f) val effects = Effects(listOf(channelMixingProcessor), listOf(rotateEffect, cropEffect)) val editedMediaItem = EditedMediaItem.Builder(mediaItem) .setEffects(effects) .build()
Ява
ChannelMixingAudioProcessor channelMixingProcessor = new ChannelMixingAudioProcessor(); ScaleAndRotateTransformation rotateEffect = new ScaleAndRotateTransformation.Builder() .setRotationDegrees(60f) .build(); Crop cropEffect = new Crop(-0.5f, 0.5f, -0.5f, 0.5f); Effects effects = new Effects( ImmutableList.of(channelMixingProcessor), ImmutableList.of(rotateEffect, cropEffect) ); EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem) .setEffects(effects) .build();
Создавайте собственные эффекты
Расширяя эффекты, включенные в Media3, вы можете создавать собственные эффекты, соответствующие вашим сценариям использования. В следующем примере используйте подкласс MatrixTransformation
, чтобы увеличить масштаб видео до заполнения кадра в течение первой секунды воспроизведения:
Котлин
val zoomEffect = MatrixTransformation { presentationTimeUs -> val transformationMatrix = Matrix() // Set the scaling factor based on the playback position val scale = min(1f, presentationTimeUs / 1_000f) transformationMatrix.postScale(/* x */ scale, /* y */ scale) transformationMatrix } val editedMediaItem = EditedMediaItem.Builder(inputMediaItem) .setEffects(Effects(listOf(), listOf(zoomEffect)) .build()
Ява
MatrixTransformation zoomEffect = presentationTimeUs -> { Matrix transformationMatrix = new Matrix(); // Set the scaling factor based on the playback position float scale = min(1f, presentationTimeUs / 1_000f); transformationMatrix.postScale(/* x */ scale, /* y */ scale); return transformationMatrix; }; EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(inputMediaItem) .setEffects(new Effects(ImmutableList.of(), ImmutableList.of(zoomEffect))) .build();
Чтобы дополнительно настроить поведение эффекта, реализуйте GlShaderProgram
. МетодqueueInputFrame queueInputFrame()
используется для обработки входных кадров. Например, чтобы использовать возможности машинного обучения MediaPipe , вы можете использовать MediaPipe FrameProcessor
для отправки каждого кадра через граф MediaPipe. См. пример этого в демонстрационном приложении Transformer .
Предварительный просмотр эффектов
С помощью ExoPlayer вы можете просмотреть эффекты, добавленные к элементу мультимедиа, перед началом процесса экспорта. Используя тот же объект Effects
, что и для EditedMediaItem
, вызовите setVideoEffects()
в своем экземпляре ExoPlayer.
Котлин
val player = ExoPlayer.builder(context) .build() .also { exoPlayer -> exoPlayer.setMediaItem(inputMediaItem) exoPlayer.setVideoEffects(effects) exoPlayer.prepare() }
Ява
ExoPlayer player = new ExoPlayer.builder(context).build(); player.setMediaItem(inputMediaItem); player.setVideoEffects(effects); exoPlayer.prepare();
Вы также можете предварительно просмотреть аудиоэффекты с помощью ExoPlayer. При создании экземпляра ExoPlayer
передайте пользовательский RenderersFactory
, который настраивает средства рендеринга звука проигрывателя для вывода звука в AudioSink
, который использует вашу последовательность AudioProcessor
. В приведенном ниже примере мы делаем это, переопределив метод buildAudioSink()
DefaultRenderersFactory
.
Котлин
val player = ExoPlayer.Builder(context, object : DefaultRenderersFactory(context) { override fun buildAudioSink( context: Context, enableFloatOutput: Boolean, enableAudioTrackPlaybackParams: Boolean, enableOffload: Boolean ): AudioSink? { return DefaultAudioSink.Builder(context) .setEnableFloatOutput(enableFloatOutput) .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) .setOffloadMode(if (enableOffload) { DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED } else { DefaultAudioSink.OFFLOAD_MODE_DISABLED }) .setAudioProcessors(arrayOf(channelMixingProcessor)) .build() } }).build()
Ява
ExoPlayer player = new ExoPlayer.Builder(context, new DefaultRenderersFactory(context) { @Nullable @Override protected AudioSink buildAudioSink( Context context, boolean enableFloatOutput, boolean enableAudioTrackPlaybackParams, boolean enableOffload ) { return new DefaultAudioSink.Builder(context) .setEnableFloatOutput(enableFloatOutput) .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) .setOffloadMode( enableOffload ? DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED : DefaultAudioSink.OFFLOAD_MODE_DISABLED) .setAudioProcessors(new AudioProcessor[]{channelMixingProcessor}) .build(); } }).build();
Начать трансформацию
Наконец, создайте Transformer
, чтобы применить ваши изменения и начать экспорт полученного медиа-элемента.
Котлин
val transformer = Transformer.Builder(context) .addListener(listener) .build() transformer.start(editedMediaItem, outputPath)
Ява
Transformer transformer = new Transformer.Builder(context) .addListener(listener) .build(); transformer.start(editedMediaItem, outputPath);
При необходимости вы также можете отменить процесс экспорта с помощью Transformer.cancel()
.
Следите за обновлениями прогресса
Transformer.start
немедленно возвращается и запускается асинхронно. Чтобы запросить текущий ход преобразования, вызовите Transformer.getProgress()
. Этот метод принимает ProgressHolder
, и если состояние прогресса доступно, то есть если метод возвращает PROGRESS_STATE_AVAILABLE
, то предоставленный ProgressHolder
будет обновлен с учетом текущего процента прогресса.
Вы также можете подключить прослушиватель к своему Transformer
, чтобы получать уведомления о событиях завершения или ошибки.
API-интерфейсы Transformer в Jetpack Media3 предназначены для повышения производительности и надежности редактирования мультимедиа. Transformer поддерживает ряд операций, в том числе:
- Изменение видео с помощью обрезки, масштабирования и вращения.
- Добавление эффектов, таких как наложения и фильтры
- Обработка специальных форматов, таких как HDR и замедленное видео.
- Экспорт медиа-элемента после применения изменений
На этой странице представлены некоторые ключевые варианты использования Transformer. Для получения более подробной информации вы можете обратиться к нашим полным руководствам по Media3 Transformer .
Начать
Для начала добавьте зависимость к модулям Transformer, Effect и Common Jetpack Media3:
implementation "androidx.media3:media3-transformer:1.5.0" implementation "androidx.media3:media3-effect:1.5.0" implementation "androidx.media3:media3-common:1.5.0"
Обязательно замените 1.5.0
предпочитаемой версией библиотеки. Вы можете обратиться к примечаниям к выпуску, чтобы увидеть последнюю версию.
Важные занятия
Сорт | Цель |
---|---|
Transformer | Запускайте и останавливайте преобразования, а также проверяйте обновления хода выполнения текущего преобразования. |
EditedMediaItem | Представляет элемент мультимедиа для обработки и изменения, которые необходимо к нему применить. |
Effects | Коллекция аудио и видео эффектов. |
Настройте вывод
С помощью Transformer.Builder
теперь вы можете указать каталог videoMimeType
и audioMimetype
, задав функцию без необходимости создания объекта TransformationRequest
.
Перекодирование между форматами
Следующий код показывает, как настроить объект Transformer
для вывода видео H.265/AVC и аудио AAC:
Котлин
val transformer = Transformer.Builder(context) .setVideoMimeType(MimeTypes.VIDEO_H265) .setAudioMimeType(MimeTypes.AUDIO_AAC) .build()
Ява
Transformer transformer = new Transformer.Builder(context) .setVideoMimeType(MimeTypes.VIDEO_H265) .setAudioMimeType(MimeTypes.AUDIO_AAC) .build();
Если формат входного мультимедиа уже соответствует запросу на преобразование аудио или видео, Transformer автоматически переключается на трансмуксирование , то есть копирование сжатых сэмплов из входного контейнера в выходной контейнер без изменений. Это позволяет избежать вычислительных затрат и потенциальной потери качества при декодировании и перекодировании в одном и том же формате.
Установить режим HDR
Если входной медиафайл имеет формат HDR, вы можете выбрать один из нескольких различных режимов обработки информации HDR Transformer. Вероятно, вы захотите использовать HDR_MODE_KEEP_HDR
или HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL
.
HDR_MODE_KEEP_HDR | HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL | |
---|---|---|
Описание | Сохраняйте данные HDR, то есть выходной формат HDR совпадает с входным форматом HDR. | Ввод Tonemap HDR в SDR с использованием преобразователя тонов OpenGL, что означает, что выходной формат будет в SDR. |
Поддерживать | Поддерживается на уровнях API 31+ для устройств, включающих кодировщик с возможностью FEATURE_HdrEditing . | Поддерживается на уровнях API 29+. |
Ошибки | Если не поддерживается, вместо этого пытается использовать HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL . | Если не поддерживается, выдает ExportException . |
На устройствах, которые поддерживают необходимые возможности кодирования и работают под управлением Android 13 (уровень API 33) или более поздней версии, объекты Transformer
позволяют редактировать HDR-видео. HDR_MODE_KEEP_HDR
— это режим по умолчанию при построении объекта Composition
, как показано в следующем коде:
Котлин
val composition = Composition.Builder( ImmutableList.of(videoSequence)) .setHdrMode(HDR_MODE_KEEP_HDR) .build()
Ява
Composition composition = new Composition.Builder( ImmutableList.of(videoSequence)) .setHdrMode(Composition.HDR_MODE_KEEP_HDR) .build();
Подготовьте медиа-материал
MediaItem
представляет собой аудио- или видеоэлемент в вашем приложении. EditedMediaItem
собирает MediaItem
вместе с преобразованиями, которые необходимо к нему применить.
Обрезать видео
Чтобы удалить ненужные части видео, вы можете установить собственные начальную и конечную позиции, добавив ClippingConfiguration
в MediaItem
.
Котлин
val clippingConfiguration = MediaItem.ClippingConfiguration.Builder() .setStartPositionMs(10_000) // start at 10 seconds .setEndPositionMs(20_000) // end at 20 seconds .build() val mediaItem = MediaItem.Builder() .setUri(videoUri) .setClippingConfiguration(clippingConfiguration) .build()
Ява
ClippingConfiguration clippingConfiguration = new MediaItem.ClippingConfiguration.Builder() .setStartPositionMs(10_000) // start at 10 seconds .setEndPositionMs(20_000) // end at 20 seconds .build(); MediaItem mediaItem = new MediaItem.Builder() .setUri(videoUri) .setClippingConfiguration(clippingConfiguration) .build();
Используйте встроенные эффекты
Media3 включает в себя ряд встроенных видеоэффектов для распространенных преобразований, например:
Сорт | Эффект |
---|---|
Presentation | Масштабируйте медиа-элемент по разрешению или соотношению сторон. |
ScaleAndRotateTransformation | Масштабируйте элемент мультимедиа с помощью множителя и/или вращайте элемент мультимедиа. |
Crop | Обрезать медиа-элемент до меньшего или большего кадра |
OverlayEffect | Добавьте наложение текста или изображения поверх элемента мультимедиа. |
Для звуковых эффектов вы можете добавить последовательность экземпляров AudioProcessor
, которые будут преобразовывать необработанные аудиоданные (PCM). Например, вы можете использовать ChannelMixingAudioProcessor
для микширования и масштабирования аудиоканалов.
Чтобы использовать эти эффекты, создайте экземпляр эффекта или аудиопроцессора, создайте экземпляр Effects
с аудио- и видеоэффектами, которые вы хотите применить к элементу мультимедиа, а затем добавьте объект Effects
в EditedMediaItem
.
Котлин
val channelMixingProcessor = ChannelMixingAudioProcessor() val rotateEffect = ScaleAndRotateTransformation.Builder().setRotationDegrees(60f).build() val cropEffect = Crop(-0.5f, 0.5f, -0.5f, 0.5f) val effects = Effects(listOf(channelMixingProcessor), listOf(rotateEffect, cropEffect)) val editedMediaItem = EditedMediaItem.Builder(mediaItem) .setEffects(effects) .build()
Ява
ChannelMixingAudioProcessor channelMixingProcessor = new ChannelMixingAudioProcessor(); ScaleAndRotateTransformation rotateEffect = new ScaleAndRotateTransformation.Builder() .setRotationDegrees(60f) .build(); Crop cropEffect = new Crop(-0.5f, 0.5f, -0.5f, 0.5f); Effects effects = new Effects( ImmutableList.of(channelMixingProcessor), ImmutableList.of(rotateEffect, cropEffect) ); EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem) .setEffects(effects) .build();
Создавайте собственные эффекты
Расширяя эффекты, включенные в Media3, вы можете создавать собственные эффекты, соответствующие вашим сценариям использования. В следующем примере используйте подкласс MatrixTransformation
, чтобы увеличить масштаб видео до заполнения кадра в течение первой секунды воспроизведения:
Котлин
val zoomEffect = MatrixTransformation { presentationTimeUs -> val transformationMatrix = Matrix() // Set the scaling factor based on the playback position val scale = min(1f, presentationTimeUs / 1_000f) transformationMatrix.postScale(/* x */ scale, /* y */ scale) transformationMatrix } val editedMediaItem = EditedMediaItem.Builder(inputMediaItem) .setEffects(Effects(listOf(), listOf(zoomEffect)) .build()
Ява
MatrixTransformation zoomEffect = presentationTimeUs -> { Matrix transformationMatrix = new Matrix(); // Set the scaling factor based on the playback position float scale = min(1f, presentationTimeUs / 1_000f); transformationMatrix.postScale(/* x */ scale, /* y */ scale); return transformationMatrix; }; EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(inputMediaItem) .setEffects(new Effects(ImmutableList.of(), ImmutableList.of(zoomEffect))) .build();
Чтобы дополнительно настроить поведение эффекта, реализуйте GlShaderProgram
. МетодqueueInputFrame queueInputFrame()
используется для обработки входных кадров. Например, чтобы использовать возможности машинного обучения MediaPipe , вы можете использовать MediaPipe FrameProcessor
для отправки каждого кадра через граф MediaPipe. См. пример этого в демонстрационном приложении Transformer .
Предварительный просмотр эффектов
С помощью ExoPlayer вы можете просмотреть эффекты, добавленные к элементу мультимедиа, перед началом процесса экспорта. Используя тот же объект Effects
, что и для EditedMediaItem
, вызовите setVideoEffects()
в своем экземпляре ExoPlayer.
Котлин
val player = ExoPlayer.builder(context) .build() .also { exoPlayer -> exoPlayer.setMediaItem(inputMediaItem) exoPlayer.setVideoEffects(effects) exoPlayer.prepare() }
Ява
ExoPlayer player = new ExoPlayer.builder(context).build(); player.setMediaItem(inputMediaItem); player.setVideoEffects(effects); exoPlayer.prepare();
Вы также можете предварительно просмотреть аудиоэффекты с помощью ExoPlayer. При создании экземпляра ExoPlayer
передайте пользовательский RenderersFactory
, который настраивает средства рендеринга звука проигрывателя для вывода звука в AudioSink
, который использует вашу последовательность AudioProcessor
. В приведенном ниже примере мы делаем это, переопределив метод buildAudioSink()
DefaultRenderersFactory
.
Котлин
val player = ExoPlayer.Builder(context, object : DefaultRenderersFactory(context) { override fun buildAudioSink( context: Context, enableFloatOutput: Boolean, enableAudioTrackPlaybackParams: Boolean, enableOffload: Boolean ): AudioSink? { return DefaultAudioSink.Builder(context) .setEnableFloatOutput(enableFloatOutput) .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) .setOffloadMode(if (enableOffload) { DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED } else { DefaultAudioSink.OFFLOAD_MODE_DISABLED }) .setAudioProcessors(arrayOf(channelMixingProcessor)) .build() } }).build()
Ява
ExoPlayer player = new ExoPlayer.Builder(context, new DefaultRenderersFactory(context) { @Nullable @Override protected AudioSink buildAudioSink( Context context, boolean enableFloatOutput, boolean enableAudioTrackPlaybackParams, boolean enableOffload ) { return new DefaultAudioSink.Builder(context) .setEnableFloatOutput(enableFloatOutput) .setEnableAudioTrackPlaybackParams(enableAudioTrackPlaybackParams) .setOffloadMode( enableOffload ? DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED : DefaultAudioSink.OFFLOAD_MODE_DISABLED) .setAudioProcessors(new AudioProcessor[]{channelMixingProcessor}) .build(); } }).build();
Начать трансформацию
Наконец, создайте Transformer
, чтобы применить ваши изменения и начать экспорт полученного медиа-элемента.
Котлин
val transformer = Transformer.Builder(context) .addListener(listener) .build() transformer.start(editedMediaItem, outputPath)
Ява
Transformer transformer = new Transformer.Builder(context) .addListener(listener) .build(); transformer.start(editedMediaItem, outputPath);
При необходимости вы также можете отменить процесс экспорта с помощью Transformer.cancel()
.
Следите за обновлениями прогресса
Transformer.start
немедленно возвращается и запускается асинхронно. Чтобы запросить текущий ход преобразования, вызовите Transformer.getProgress()
. Этот метод принимает ProgressHolder
, и если состояние прогресса доступно, то есть если метод возвращает PROGRESS_STATE_AVAILABLE
, то предоставленный ProgressHolder
будет обновлен с учетом текущего процента прогресса.
Вы также можете подключить прослушиватель к своему Transformer
, чтобы получать уведомления о событиях завершения или ошибки.