linkedin / LiTr

Lightweight hardware accelerated video/audio transcoder for Android.
BSD 2-Clause "Simplified" License
609 stars 85 forks source link

need guidance for implementing scaling and adjusting bitrate of video. #25

Open avanjv opened 4 years ago

avanjv commented 4 years ago

Need guidance for implementing scaling video while keeping the aspect ratio and adjusting bitrate to the given value of a video file. Can you provide methods to be used for scaling and adjusting bitrate of a video file?

Thank you in advance!!

izzytwosheds commented 4 years ago

LiTr already allows you to do all of those things - you can specify any combination of width/height/bitrate in target MediaFormat. It sounds like you are asking for a helper method to calculate a target video width/height which maintain the aspect ratio of a source video. That can be done. As for bitrate, can you elaborate more? Current assumption is that client app sets it itself, to be able to control the target video quality/size. Do you have a different criteria for it?

avanjv commented 4 years ago

Yes we are trying to adjust the bitrate of a video. for example: lets say video has bitrate of 1000k after transcoding bitrate reduces to 700k. I went through the provided demo but I couldn't find my method related to change bitrate of a video file to a specific number. I just need help with methods or can you just provide an example with method.

Thanks a ton in advance!

izzytwosheds commented 4 years ago

Sample app uses more complex track based API. For a simple transformation it is better to use a simpler transform API: public void transform(@NonNull String requestId, @NonNull Uri inputUri, @NonNull String outputFilePath, @Nullable MediaFormat targetVideoFormat, @Nullable MediaFormat targetAudioFormat, @NonNull TransformationListener listener, @IntRange(from = GRANULARITY_NONE) int granularity, @Nullable List filters)

targetAudioFormat and filters can be null, granularity should probably be GRANULARITY_DEFAULT

Main thing is configuring targetVideoFormat. Here is sample code: MediaFormat targetVideoFormat = new MediaFormat(); targetVideoFormat.setString(MediaFormat.KEY_MIME, "video/avc"); targetVideoFormat.setInteger(MediaFormat.KEY_WIDTH, targetWidth); targetVideoFormat.setInteger(MediaFormat.KEY_HEIGHT, targetHeight); targetVideoFormat.setInteger(MediaFormat.KEY_BIT_RATE, targetBitrate); targetVideoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); targetVideoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30); targetVideoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);

Set your target width, height and bitrate correctly, everything should work.

izzytwosheds commented 4 years ago

Were you guys able to get transcoding working?

avanjv commented 4 years ago

We were trying to reduce the video file size by resizing to a specific resolution while keeping the aspect ratio. We tried litr lib on different android devices, but on a few devices i.e Samsung j2 etc the resolution of video will change to specific values neither the bit is changing. For example: Video file of resolution 1000x2000 px bit rate 2600k kbps we tried to transcode it to 720x1440 px and bit rate 800k , the litr wont proceed with this value for transcoding. but if i change values to 500x1000 px it transcode. But on other android devices it will transcode with required values ( 720x1440 px ). Also on Samsung j2 device FFmpeg is able to transcode video to 720x1440 px and bit rate 800k kbps. Right now we are using FFmpeg fro this, but the processing speed is very low. And litr lb wont transcode video to specific values on few devices and some android versions. Is the target values dependent on the device? Are there any limitations for targetvalues?

izzytwosheds commented 4 years ago

Under the hood LiTr uses MediaCodec stack, which usually provides access to encoding hardware on a device. This provides real-time performance at the expense of compatibility - hardware will not always support every codec/resolution/bitrate. Software codec implementations, such as ones used in ffmpeg are the opposite.

It looks like that Samsung J2 being a lower end device doesn't want to resize the frame with interpolation. In your case, scaling down from 1000 to 500 is very quick and easy - just skip every other pixel. Downscaling from 1000 to 720 will take a lot more computations, since now we have to interpolate between pixels. So device seems to be staying on a cautious side and refusing to transcode if it feels like it will not be able to provide "real time" performance.

Can you post a logcat snippet with LiTr exception? LiTr will provide an exception when it hits an error.

alexvasilkov commented 3 years ago

Returning to the original topic (guidance for scaling and adjusting bitrate), I think it will indeed be very helpful to have some kind of utility that would help with scaling the video preserving aspect ratio. It may sound as an easy task but looking into this implementation from another library it becomes clear that proper logic is more complicated than one would expect.

I think bitrate can also be automtically adjusted according to the final video size. E.g. if I want 3mbps for 1280x720 video but original video was 960x540, then I would like the final video to be 960x540 with 3 * (960 * 540) / (1280 * 720) = 1.69mbps bitrate. I'm not really sure that bitrate can be calculated like this, but I don't think using 3mbps for a smaller video is a good option as well.

Also (off-topic) I want to mention the general lack of documentation / usage examples. It makes it hard to start using the library while the library itself is very good! The demo app only makes it harder to understand because it contains a lot of boilerplate code to support its own logic, and having part of the logic in xml files (because of view bindings) does not help as well. Sorry for ranting, and thanks for the great library :)

izzytwosheds commented 3 years ago

Glad you are enjoying LiTr! No apologies necessary, you are right on point on both issues.

I have been thinking recently about adding a utility to help create properly configured MediaFormat based on what user wants. That is coming very soon. Special thank you for your suggestion to calculate adjusted bitrate, that is very nifty.

I have been meaning to start a wiki on LiTr about its new functionality. When I initially wrote it, it could only transcode, so README.md provided enough documentation. But now LiTr does a lot more, so more documentation is required. That is coming soon, too!

alexvasilkov commented 3 years ago

Great! An utility to properly configure MediaFormat will be very useful, as it will help to avoid some common issues (like using odd width or height) and can simplify overall MediaFormat usage.

I'm thinking that maybe it can also help to deal with HEVC codec? I would like to use HEVC encoder on devices that have hardware support and fallback to AVC otherwise. We are also using lower bitrate for HEVC videos. Not sure if it's a common use case though. Anyway, this is how I'm doing it now (it is not tested on many devices yet):

fun hasHardwareHevcEncoder(): Boolean {
  val mediaCodecList = MediaCodecList(MediaCodecList.REGULAR_CODECS)
  for (codec in mediaCodecList.codecInfos) {
    if (codec.isEncoder && codec.isHevc() && codec.isHardware()) return true
  }
  return false
}

private fun MediaCodecInfo.isHevc(): Boolean {
  for (type in supportedTypes) {
    if (getCapabilitiesForType(type).mimeType == MediaFormat.MIMETYPE_VIDEO_HEVC) return true
  }
  return false
}

/** Source: [com.google.android.exoplayer2.mediacodec.MediaCodecUtil]. */
private fun MediaCodecInfo.isHardware() = if (Build.VERSION.SDK_INT >= 29) {
  isHardwareAccelerated
} else {
  !isSoftware()
}

/** Source: [com.google.android.exoplayer2.mediacodec.MediaCodecUtil]. */
private fun MediaCodecInfo.isSoftware(): Boolean = with(name.lowercase()) {
  if (startsWith("arc.")) { // App Runtime for Chrome (ARC) codecs
    false
  } else {
    startsWith("omx.google.") ||
      startsWith("omx.ffmpeg.") ||
      (startsWith("omx.sec.") && contains(".sw.")) ||
      this == "omx.qcom.video.decoder.hevcswvdec" ||
      startsWith("c2.android.") ||
      startsWith("c2.google.") ||
      (!startsWith("omx.") && !startsWith("c2."))
  }
}