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.mediatool.demos; 021 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024 025import java.io.File; 026import java.nio.ShortBuffer; 027 028import java.awt.Color; 029import java.awt.Graphics2D; 030import java.awt.geom.Rectangle2D; 031import java.awt.image.BufferedImage; 032 033import com.avpkit.mediatool.IMediaTool; 034import com.avpkit.mediatool.ToolFactory; 035import com.avpkit.mediatool.IMediaReader; 036import com.avpkit.mediatool.IMediaWriter; 037import com.avpkit.mediatool.MediaToolAdapter; 038import com.avpkit.mediatool.event.IAudioSamplesEvent; 039import com.avpkit.mediatool.event.IVideoPictureEvent; 040 041/** 042 * Read and modify audio and video frames and use the {@link 043 * IMediaWriter} to encode that media and write it out to a file. 044 * 045 * @author trebor 046 * @author aclarke 047 */ 048public class ModifyAudioAndVideo 049{ 050 // the log 051 052 private static final Logger log = LoggerFactory.getLogger( 053 ModifyAudioAndVideo.class); 054 { log.trace("<init>"); } 055 056 /** 057 * Create and display a number of bouncing balls on the 058 */ 059 060 public static void main(String[] args) 061 { 062 if (args.length < 2) 063 { 064 System.out.println( 065 "Usage: ModifyAudioAndVideo <inputFileName> <outputFileName>"); 066 System.exit(-1); 067 } 068 069 File inputFile = new File(args[0]); 070 if (!inputFile.exists()) 071 { 072 System.out.println("Input file does not exist: " + inputFile); 073 System.exit(-1); 074 } 075 076 File outputFile = new File(args[1]); 077 078 // create a media reader and configure it to generate BufferImages 079 080 IMediaReader reader = ToolFactory.makeReader(inputFile.toString()); 081 reader.setBufferedImageTypeToGenerate(BufferedImage.TYPE_3BYTE_BGR); 082 083 // create a writer and configure it's parameters from the reader 084 085 IMediaWriter writer = ToolFactory.makeWriter(outputFile.toString(), reader); 086 087 // create a tool which paints video time stamp into frame 088 089 IMediaTool addTimeStamp = new TimeStampTool(); 090 091 // create a tool which reduces audio volume to 1/10th original 092 093 IMediaTool reduceVolume = new VolumeAdjustTool(0.1); 094 095 // create a tool chain: 096 // reader -> addTimeStamp -> reduceVolume -> writer 097 098 reader.addListener(addTimeStamp); 099 addTimeStamp.addListener(reduceVolume); 100 reduceVolume.addListener(writer); 101 102 // add a viewer to the writer, to see media modified media 103 104 writer.addListener(ToolFactory.makeViewer()); 105 106 // read and decode packets from the source file and 107 // then encode and write out data to the output file 108 109 while (reader.readPacket() == null) 110 do {} while(false); 111 } 112 113 /** 114 * Create a tool which adds a time stamp to a video image. 115 */ 116 117 static class TimeStampTool extends MediaToolAdapter 118 { 119 /** {@inheritDoc} */ 120 121 @Override 122 public void onVideoPicture(IVideoPictureEvent event) 123 { 124 // get the graphics for the image 125 126 Graphics2D g = event.getImage().createGraphics(); 127 128 // establish the timestamp and how much space it will take 129 130 String timeStampStr = event.getPicture().getFormattedTimeStamp(); 131 Rectangle2D bounds = g.getFont().getStringBounds(timeStampStr, 132 g.getFontRenderContext()); 133 134 // compute the amount to inset the time stamp and translate the 135 // image to that position 136 137 double inset = bounds.getHeight() / 2; 138 g.translate(inset, event.getImage().getHeight() - inset); 139 140 // draw a white background and black timestamp text 141 142 g.setColor(Color.WHITE); 143 g.fill(bounds); 144 g.setColor(Color.BLACK); 145 g.drawString(timeStampStr, 0, 0); 146 147 // call parent which will pass the video onto next tool in chain 148 149 super.onVideoPicture(event); 150 } 151 } 152 153 /** 154 * Create a tool which adjusts the volume of audio by some constant factor. 155 */ 156 157 static class VolumeAdjustTool extends MediaToolAdapter 158 { 159 // the amount to adjust the volume by 160 161 private double mVolume; 162 163 /** 164 * Construct a volume adjustor. 165 * 166 * @param volume the volume muliplier, values between 0 and 1 are 167 * recommended. 168 */ 169 170 public VolumeAdjustTool(double volume) 171 { 172 mVolume = volume; 173 } 174 175 /** {@inheritDoc} */ 176 177 @Override 178 public void onAudioSamples(IAudioSamplesEvent event) 179 { 180 // get the raw audio byes and adjust it's value 181 182 ShortBuffer buffer = event.getAudioSamples().getByteBuffer().asShortBuffer(); 183 for (int i = 0; i < buffer.limit(); ++i) 184 buffer.put(i, (short)(buffer.get(i) * mVolume)); 185 186 // call parent which will pass the audio onto next tool in chain 187 188 super.onAudioSamples(event); 189 } 190 } 191}