001/******************************************************************************* 002 * Copyright (c) 2024, 2026, Olivier Ayache. All rights reserved. 003 * 004 * This file is part of AVPKit. 005 * 006 * AVPKit is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU Lesser General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * AVPKit is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with AVPKit. If not, see <http://www.gnu.org/licenses/>. 018 *******************************************************************************/ 019 020package com.avpkit.core.video; 021 022import java.awt.image.BufferedImage; 023import com.avpkit.core.IVideoPicture; 024import com.avpkit.core.IVideoResampler; 025import com.avpkit.core.IPixelFormat; 026 027/** An abstract converter class from which specific converters can be 028 * derived to do the actual conversions. This class establishes if 029 * the {@link IVideoPicture} needs to be re-sampled, and 030 * if so, creates appropriate {@link IVideoResampler} objects to do 031 * that. 032 */ 033 034abstract public class AConverter implements IConverter 035{ 036 /** Re-sampler called when converting image to picture, may be null. */ 037 038 protected IVideoResampler mToPictureResampler = null; 039 040 /** Re-sampler called when converting picture to image, may be null. */ 041 042 protected IVideoResampler mToImageResampler = null; 043 044 /** The width of the pictures. */ 045 046 protected int mPictureWidth; 047 048 /** The height of the pictures. */ 049 050 protected int mPictureHeight; 051 052 /** The width of the images. */ 053 054 protected int mImageWidth; 055 056 /** The height of the images. */ 057 058 protected int mImageHeight; 059 060 // the recognized BufferedImage type 061 062 final private IPixelFormat.Type mPictureType; 063 064 // the recognized BufferedImage type 065 066 final private IPixelFormat.Type mRequiredPictureType; 067 068 // the recognized BufferedImage type 069 070 final private int mImageType; 071 072 // the description of this convert 073 074 final private String mDescription; 075 076 /** 077 * Construct an abstract Converter. This will create a 078 * {@link IVideoResampler} 079 * to change color-space or resize the picture as needed for the 080 * conversions specified. 081 * 082 * @param pictureType the recognized {@link IVideoPicture} type 083 * @param requiredPictureType the picture type requred to translate to 084 * and from the BufferedImage 085 * @param imageType the recognized {@link BufferedImage} type 086 * @param pictureWidth the width of picture 087 * @param pictureHeight the height of picture 088 * @param imageWidth the width of image 089 * @param imageHeight the height of image 090 */ 091 092 public AConverter( 093 IPixelFormat.Type pictureType, 094 IPixelFormat.Type requiredPictureType, 095 int imageType, 096 int pictureWidth, 097 int pictureHeight, 098 int imageWidth, 099 int imageHeight) 100 { 101 // by default there is no resample description 102 103 String resampleDescription = ""; 104 105 // record the image and picture parameters 106 107 mPictureType = pictureType; 108 mRequiredPictureType = requiredPictureType; 109 mImageType = imageType; 110 mPictureWidth = pictureWidth; 111 mPictureHeight = pictureHeight; 112 mImageWidth = imageWidth; 113 mImageHeight = imageHeight; 114 115 // if the picture type is not the type or size required, create the 116 // resamplers to fix that 117 118 if (!pictureType.equals(requiredPictureType) 119 || (mPictureWidth != mImageWidth) 120 || (mPictureHeight != mImageHeight)) 121 { 122 if (!IVideoResampler.isSupported( 123 IVideoResampler.Feature.FEATURE_COLORSPACECONVERSION)) 124 throw new RuntimeException( 125 "0 Could not create could resampler to translate from " + 126 pictureType + " to " + requiredPictureType + ". " + 127 "Color space conversion is not supported by this version of" + 128 "AVPKit. Recompile AVPKit with the GPL option enabled."); 129 130 mToImageResampler = IVideoResampler.make( 131 imageWidth, imageHeight, requiredPictureType, 132 mPictureWidth, mPictureHeight, pictureType); 133 134 if (mToImageResampler == null) 135 throw new RuntimeException( 136 "1 Could not create could resampler to translate from " + 137 pictureType + " to " + requiredPictureType + "."); 138 139 mToPictureResampler = IVideoResampler.make( 140 mPictureWidth, mPictureHeight, pictureType, 141 imageWidth, imageHeight, requiredPictureType); 142 143 if (mToPictureResampler == null) 144 throw new RuntimeException( 145 "2 Could not create could resampler to translate from " + 146 requiredPictureType + " to " + pictureType); 147 148 resampleDescription = "Pictures will be resampled to and from " + 149 requiredPictureType + " during translation."; 150 } 151 152 // construct the description of this converter 153 154 mDescription = "A converter which translates [" + 155 pictureWidth + "x" + pictureHeight + "] IVideoPicture type " + 156 pictureType + " to and from [" + imageWidth + "x" + imageHeight + 157 "] BufferedImage type " + imageType + ". " + resampleDescription; 158 } 159 160 /** {@inheritDoc} */ 161 162 public IPixelFormat.Type getPictureType() 163 { 164 return mPictureType; 165 } 166 167 /** 168 * Return the Type which matches the {@link 169 * BufferedImage} type. 170 * 171 * @return the picture type which allows for image translation. 172 */ 173 174 protected IPixelFormat.Type getRequiredPictureType() 175 { 176 return mRequiredPictureType; 177 } 178 179 /** {@inheritDoc} */ 180 181 public int getImageType() 182 { 183 return mImageType; 184 } 185 186 /** {@inheritDoc} */ 187 188 public boolean willResample() 189 { 190 return null != mToPictureResampler && null != mToImageResampler; 191 } 192 193 /** 194 * Re-sample a picture. 195 * 196 * @param picture1 the picture to re-sample 197 * @param resampler the picture re-samper to use 198 * 199 * @throws RuntimeException if could not re-sample picture 200 **/ 201 202 protected static IVideoPicture resample(IVideoPicture picture1, 203 IVideoResampler resampler) 204 { 205 // create new picture object 206 207 IVideoPicture picture2 = IVideoPicture.make( 208 resampler.getOutputPixelFormat(), 209 resampler.getOutputWidth(), 210 resampler.getOutputHeight()); 211 212 // resample 213 214 if (resampler.resample(picture2, picture1) < 0) 215 throw new RuntimeException( 216 "could not resample from " + resampler.getInputPixelFormat() + 217 " to " + resampler.getOutputPixelFormat() + 218 " for picture of type " + picture1.getPixelType()); 219 220 // test that it worked 221 222 if (picture2.getPixelType() != resampler.getOutputPixelFormat() 223 || !picture2.isComplete()) 224 { 225 throw new RuntimeException( 226 "did not resample from " + resampler.getInputPixelFormat() + 227 " to " + resampler.getOutputPixelFormat() + 228 " for picture of type " + picture1.getPixelType()); 229 } 230 231 // return the resample picture 232 233 return picture2; 234 } 235 236 /** 237 * Test that the passed image is valid and conforms to the 238 * converters specifications. 239 * 240 * @param image the image to test 241 * 242 * @throws IllegalArgumentException if the passed {@link 243 * BufferedImage} is NULL; 244 * @throws IllegalArgumentException if the passed {@link 245 * BufferedImage} is not the correct type. See {@link 246 * #getImageType}. 247 */ 248 249 protected void validateImage(BufferedImage image) 250 { 251 // if the image is NULL, throw up 252 253 if (image == null) 254 throw new IllegalArgumentException("The passed image is NULL."); 255 256 // if image is not the correct type, throw up 257 258 if (image.getType() != getImageType()) 259 throw new IllegalArgumentException( 260 "The passed image is of type #" + image.getType() + 261 " but is required to be of BufferedImage type #" + 262 getImageType() + "."); 263 } 264 265 /** 266 * Test that the passed picture is valid and conforms to the 267 * converters specifications. 268 * 269 * @param picture the picture to test 270 * 271 * @throws IllegalArgumentException if the passed {@link 272 * IVideoPicture} is NULL; 273 * @throws IllegalArgumentException if the passed {@link 274 * IVideoPicture} is not complete. 275 * @throws IllegalArgumentException if the passed {@link 276 * IVideoPicture} is not the correct type. 277 */ 278 279 protected void validatePicture(IVideoPicture picture) 280 { 281 // if the pictre is NULL, throw up 282 283 if (picture == null) 284 throw new IllegalArgumentException("The picture is NULL."); 285 286 // if the picture is not complete, throw up 287 288 if (!picture.isComplete()) 289 throw new IllegalArgumentException("The picture is not complete."); 290 291 // if the picture is an invalid type throw up 292 293 IPixelFormat.Type type = picture.getPixelType(); 294 if ((type != getPictureType()) && (willResample() && 295 type != mToImageResampler.getOutputPixelFormat())) 296 throw new IllegalArgumentException( 297 "Picture is of type: " + type + ", but must be " + 298 getPictureType() + (willResample() 299 ? " or " + mToImageResampler.getOutputPixelFormat() 300 : "") + 301 "."); 302 } 303 304 /** {@inheritDoc} */ 305 306 public String getDescription() 307 { 308 return mDescription; 309 } 310 311 /** Get a string representation of this converter. */ 312 313 public String toString() 314 { 315 return getDescription(); 316 } 317 318 public void close() 319 { 320 if (mToPictureResampler != null) 321 mToPictureResampler.delete(); 322 mToPictureResampler = null; 323 if (mToImageResampler != null) 324 mToImageResampler.delete(); 325 mToImageResampler = null; 326 } 327 328}