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.awt.image.BufferedImage; 026 027import com.avpkit.mediatool.IMediaViewer; 028import com.avpkit.mediatool.IMediaWriter; 029import com.avpkit.mediatool.ToolFactory; 030import com.avpkit.core.IAudioSamples; 031 032import static java.util.concurrent.TimeUnit.SECONDS; 033import static java.util.concurrent.TimeUnit.MILLISECONDS; 034import static com.avpkit.core.Global.DEFAULT_TIME_UNIT; 035 036/** 037 * Generate audio and video frames and use the {@link IMediaWriter} to 038 * encode that media and write it out to a file. 039 * 040 * @author trebor 041 * @author aclarke 042 */ 043 044public class GenerateAudioAndVideo 045{ 046 // the log 047 048 private static final Logger log = LoggerFactory.getLogger( 049 GenerateAudioAndVideo.class); 050 { log.trace("<init>"); } 051 052 /** 053 * Create and display a number of bouncing balls on the 054 */ 055 056 public static void main(String[] args) 057 { 058 // the number of balls to bounce around 059 060 final int ballCount = 2; 061 062 // total duration of the media 063 064 final long duration = DEFAULT_TIME_UNIT.convert(60, SECONDS); 065 066 // video parameters 067 068 final int videoStreamIndex = 0; 069 final int videoStreamId = 0; 070 final long frameRate = DEFAULT_TIME_UNIT.convert(15, MILLISECONDS); 071 final int width = 320; 072 final int height = 200; 073 074 // audio parameters 075 076 final int audioStreamIndex = 1; 077 final int audioStreamId = 0; 078 final int channelCount = 1; 079 final int sampleRate = 44100; // Hz 080 final int sampleCount = 1000; 081 082 // the clock time of the next frame 083 084 long nextFrameTime = 0; 085 086 // the total number of audio samples 087 088 long totalSampleCount = 0; 089 090 // create a media writer and specify the output file 091 092 final IMediaWriter writer = ToolFactory.makeWriter("myballs.mov"); 093 094 // add a viewer so we can see the media as it is created 095 096 writer.addListener(ToolFactory.makeViewer( 097 IMediaViewer.Mode.AUDIO_VIDEO, true, 098 javax.swing.WindowConstants.EXIT_ON_CLOSE)); 099 100 // add the video stream 101 102 writer.addVideoStream(videoStreamIndex, videoStreamId, 103 width, height); 104 105 // add the audio stream 106 107 writer.addAudioStream(audioStreamIndex, audioStreamId, 108 channelCount, sampleRate); 109 110 // create some balls to show on the screen 111 112 Balls balls = new MovingBalls(ballCount, width, height, sampleCount); 113 114 // loop through clock time, which starts at zero and increases based 115 // on the total number of samples created thus far 116 117 for (long clock = 0; clock < duration; clock = IAudioSamples 118 .samplesToDefaultPts(totalSampleCount, sampleRate)) 119 { 120 // while the clock time exceeds the time of the next video frame, 121 // get and encode the next video frame 122 123 while (clock >= nextFrameTime) 124 { 125 BufferedImage frame = balls.getVideoFrame(frameRate); 126 writer.encodeVideo(videoStreamIndex, frame, nextFrameTime, 127 DEFAULT_TIME_UNIT); 128 nextFrameTime += frameRate; 129 } 130 131 // compute and encode the audio for the balls 132 133 short[] samples = balls.getAudioFrame(sampleRate); 134 writer.encodeAudio(audioStreamIndex, samples, clock, 135 DEFAULT_TIME_UNIT); 136 totalSampleCount += sampleCount; 137 } 138 139 // manually close the writer 140 141 writer.close(); 142 } 143}