Je dois me mettre a la creation de video a partir d'un set d'images (pre-generees ou generees au vol) et je dois avouer que ce n'est pas trop mon domaine de competences habituelles et donc que j'y vais a tatons et j'ai du mal a trouver les infos que je cherche. J'ai quand meme trouve ce code sur les forums de Sun (http://forum.java.sun.com/thread.jsp...sageID=2452394) qui est une correction d'un exemple poste dans le didacticiel du Java Media Framework (http://java.sun.com/products/java-me...sToMovie.html). Une fois adapte a mes besoins de test (c'est encore assez simpliste et j'ai fait peu de modifications) :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
 
package test;
 
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.util.*;
import javax.imageio.*;
import javax.media.*;
import javax.media.control.*;
import javax.media.datasink.*;
import javax.media.format.*;
import javax.media.protocol.*;
 
/**For AVI files, each frame must have a time stamp set.See the following message from the jmf - interest archives for details : http : //archives.java.sun.com/cgi-bin/wa?A2=ind0107&L=jmf-interest&P=R34660
 */
public class AviCreator implements ControllerListener, DataSinkListener {
  private boolean doIt(int width, int height, int frameRate, MediaLocator outML) {
    ImageDataSource ids = new ImageDataSource(width, height, frameRate);
    Processor p;
    try {
      System.err.println("- create processor for the image datasource ...");
      p = Manager.createProcessor(ids);
    }
    catch (Exception e) {
      System.err.println("Yikes! Cannot create a processor from the data source.");
      return false;
    }
    p.addControllerListener(this);
    // Put the Processor into configured state so we can set some processing options on the processor.
    p.configure();
    if (!waitForState(p, Processor.Configured)) {
      System.err.println("Failed to configure the processor.");
      return false;
    }
    // Set the output content descriptor to QuickTime.
    //p.setContentDescriptor(new ContentDescriptor(FileTypeDescriptor.MSVIDEO));
    p.setContentDescriptor(new ContentDescriptor(FileTypeDescriptor.QUICKTIME));
    // Query for the processor for supported formats.
    // Then set it on the processor.
    TrackControl tcs[] = p.getTrackControls();
    Format f[] = tcs[0].getSupportedFormats();
    if (f == null || f.length <= 0) {
      System.err.println("The mux does not support the input format: " + tcs[0].getFormat());
      return false;
    }
    tcs[0].setFormat(f[0]);
    System.err.println("Setting the track format to: " + f[0]);
    // We are done with programming the processor. Let's just realize it.
    p.realize();
    if (!waitForState(p, Processor.Realized)) {
      System.err.println("Failed to realize the processor.");
      return false;
    }
    // Now, we'll need to create a DataSink.
    DataSink dsink;
    if ((dsink = createDataSink(p, outML)) == null) {
      System.err.println("Failed to create a DataSink for the given output MediaLocator: " + outML);
      return false;
    }
    dsink.addDataSinkListener(this);
    fileDone = false;
    System.err.println("start processing...");
    // OK, we can now start the actual transcoding.
    try {
      p.start();
      dsink.start();
    }
    catch (IOException e) {
      System.err.println("IO error during processing");
      return false;
    }
    // Wait for EndOfStream event.
    waitForFileDone();
    // Cleanup.
    try {
      dsink.close();
    }
    catch (Exception e) {
    }
    p.removeControllerListener(this);
    System.err.println("...done processing.");
    return true;
  }
 
  /** Create the DataSink.
   */
  private DataSink createDataSink(Processor p, MediaLocator outML) {
    DataSource ds;
    if ((ds = p.getDataOutput()) == null) {
      System.err.println("Something is really wrong: the processor does not have an output DataSource");
      return null;
    }
    DataSink dsink;
    try {
      System.err.println("- create DataSink for: " + outML);
      dsink = Manager.createDataSink(ds, outML);
      dsink.open();
    }
    catch (Exception e) {
      System.err.println("Cannot create the DataSink: " + e);
      return null;
    }
    return dsink;
  }
 
  private Object waitSync = new Object();
 
  private boolean stateTransitionOK = true;
 
  /** Block until the processor has transitioned to the given state.
   * Return false if the transition failed.
   */
  private boolean waitForState(Processor p, int state) {
    synchronized (waitSync) {
      try {
        while (p.getState() < state && stateTransitionOK) {
          waitSync.wait();
        }
      }
      catch (Exception e) {
      }
    }
    return stateTransitionOK;
  }
 
  /** Controller Listener.
   */
  public void controllerUpdate(ControllerEvent evt) {
 
    if (evt instanceof ConfigureCompleteEvent || evt instanceof RealizeCompleteEvent || evt instanceof PrefetchCompleteEvent) {
      synchronized (waitSync) {
        stateTransitionOK = true;
        waitSync.notifyAll();
      }
    }
    else if (evt instanceof ResourceUnavailableEvent) {
      synchronized (waitSync) {
        stateTransitionOK = false;
        waitSync.notifyAll();
      }
    }
    else if (evt instanceof EndOfMediaEvent) {
      evt.getSourceController().stop();
      evt.getSourceController().close();
    }
  }
 
  private Object waitFileSync = new Object();
 
  private boolean fileDone = false;
 
  private boolean fileSuccess = true;
 
  /** Block until file writing is done.
   */
  private boolean waitForFileDone() {
    synchronized (waitFileSync) {
      try {
        while (!fileDone) {
          waitFileSync.wait();
        }
      }
      catch (Exception e) {
      }
    }
    return fileSuccess;
  }
 
  /**
   * Event handler for the file writer.
   */
  public void dataSinkUpdate(DataSinkEvent evt) {
    if (evt instanceof EndOfStreamEvent) {
      synchronized (waitFileSync) {
        fileDone = true;
        waitFileSync.notifyAll();
      }
    }
    else if (evt instanceof DataSinkErrorEvent) {
      synchronized (waitFileSync) {
        fileDone = true;
        fileSuccess = false;
        waitFileSync.notifyAll();
      }
    }
  }
 
  /** Self-test main.
   * @param args Arguments from the command-line.
   * @exception java.lang.Exception case of errors.
   */
  public static void main(String args[]) throws Exception {
    //jpegCreator.main(null);
    //if (args.length == 0)
    // prUsage();
    // Parse the arguments.
    int i = 0;
    int width = -1, height = -1, frameRate = 1;
    //Vector inputFiles = new Vector();
    String outputURL = null;
    //width = 658;
    //height = 573;
    width = 657;
    height = 573;
    //outputURL = "test.avi";
    outputURL = "test.mov";
    // Generate the output media locators.
    MediaLocator oml;
    if ((oml = createMediaLocator(outputURL)) == null) {
      System.err.println("Cannot build media locator from: " + outputURL);
      System.exit(1);
    }
    AviCreator imageToMovie = new AviCreator();
    imageToMovie.doIt(width, height, frameRate, oml);
    System.exit(0);
  }
 
  /*
     static void prUsage() {
    System.err.println("Usage: java JpegImagesToMovie -w <width> -h <height> -f <frame rate> -o <output URL> <input JPEG file 1> <input JPEG file 2> ...");
    System.exit( -1);
     }
   */
 
  /** Create a media locator from the given string.
   */
  // Allows JMF to locate output file.
  private static MediaLocator createMediaLocator(String url) {
    MediaLocator ml;
    if (url.indexOf(":") > 0 && (ml = new MediaLocator(url)) != null) {
      return ml;
    }
    if (url.startsWith(File.separator)) {
      if ((ml = new MediaLocator("file:" + url)) != null) {
        return ml;
      }
    }
    else {
      String file = "file:" + System.getProperty("user.dir") + File.separator + url;
      if ((ml = new MediaLocator(file)) != null) {
        return ml;
      }
    }
    return null;
  }
 
  ///////////////////////////////////////////////
  //
  // Inner classes.
  ///////////////////////////////////////////////
 
  /** A DataSource to read from a list of JPEG image files and turn that into a stream of JMF buffers.
   * The DataSource is not seekable or positionable.
   */
  private class ImageDataSource extends PullBufferDataSource {
 
    private ImageSourceStream[] streams;
 
    ImageDataSource(int width, int height, int frameRate) {
      streams = new ImageSourceStream[1];
      streams[0] = new ImageSourceStream(width, height, frameRate);
    }
 
    public void setLocator(MediaLocator source) {
    }
 
    public MediaLocator getLocator() {
      return null;
    }
 
    /** Content type is of RAW since we are sending buffers of video frames without a container format.
     */
    public String getContentType() {
      return ContentDescriptor.RAW;
    }
 
    public void connect() {
    }
 
    public void disconnect() {
    }
 
    public void start() {
    }
 
    public void stop() {
    }
 
    /** Return the ImageSourceStreams.
     */
    public PullBufferStream[] getStreams() {
      return streams;
    }
 
    /** We could have derived the duration from the number of frames and frame rate. But for the purpose of this program, it's not necessary.
     */
    public Time getDuration() {
      System.out.println("dur is " + streams[0].nextImage);
      //return new Time(1000000000);
      return DURATION_UNKNOWN;
    }
 
    public Object[] getControls() {
      return new Object[0];
    }
 
    public Object getControl(String type) {
      return null;
    }
  }
 
 
  /** The source stream to go along with ImageDataSource.
   */
  class ImageSourceStream implements PullBufferStream {
 
    private final int width, height;
 
    private final VideoFormat format;
 
    private BufferedImage frame = null;
 
    // Bug fix from Forums - next two lines
    private float frameRate;
    private long seqNo = 0;
 
    private int nextImage = 0; // index of the next image to be read.
    private boolean ended = false;
 
    public ImageSourceStream(int width, int height, int frameRate) {
      this.width = width;
      this.height = height;
 
      // Bug fix from Forums (next line)
      this.frameRate = (float) frameRate;
 
      final int rMask = 0x00ff0000;
      final int gMask = 0x0000FF00;
      final int bMask = 0x000000ff;
 
      format = new javax.media.format.RGBFormat(new Dimension(width, height), Format.NOT_SPECIFIED, Format.intArray, frameRate, 32, rMask, gMask, bMask);
    }
 
    /**
     * We should never need to block assuming data are read from files.
     */
    public boolean willReadBlock() {
      return false;
    }
 
    /** This is called from the Processor to read a frame worth of video data.
     */
    public void read(Buffer buf) throws IOException {
      // Check if we've finished all the frames.
      if (nextImage >= 252) {
        // We are done. Set EndOfMedia.
        System.err.println("Done reading all images.");
        buf.setEOM(true);
        buf.setOffset(0);
        buf.setLength(0);
        ended = true;
        return;
      }
      int max = (int) Math.log10(252);
      int current = (nextImage != 0) ? (int) Math.log10(nextImage) : 0;
      StringBuilder b = new StringBuilder("image");
      for (int i = current; i < max; i++) {
        b.append(0);
      }
      b.append(nextImage).append(".png");
      System.out.println(b.toString());
      BufferedImage image = ImageIO.read(new File(b.toString()));
      if ((image.getWidth() != width) || (image.getHeight() != height)) {
        // Lazily create frame of the correct size.
        if (frame == null) {
          frame = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        }
        Graphics2D graphics = frame.createGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        graphics.drawImage(image, 0, 0, width, height, null);
        graphics.dispose();
      }
      else {
        frame = image;
      }
      nextImage++;
      int[] data = null;
      if (buf.getData() instanceof int[]) {
        data = (int[]) buf.getData();
      }
      // Check to see the given buffer is big enough for the frame.
      if (data == null || data.length < width * height) {
        data = new int[width * height];
        buf.setData(data);
      }
      data = frame.getRGB(0, 0, width, height, data, 0, width);
      // Bug fix from Forums ( next 3 lines).
      long time = (long) (seqNo * (1000 / frameRate) * 1000000);
      buf.setTimeStamp(time);
      buf.setSequenceNumber(seqNo++);
      buf.setData(data);
      buf.setOffset(0);
      buf.setLength(width * height);
      buf.setFormat(format);
      buf.setFlags(buf.getFlags() | Buffer.FLAG_KEY_FRAME);
    }
 
    /** This is called from the Processor to read a frame worth of video data.
     */
    /*
         public void read(Buffer buf) throws IOException {
             // Check if we've finished all the frames.
             if (nextImage >= 100) {
        // We are done. Set EndOfMedia.
        System.err.println("Done reading all images.");
        buf.setEOM(true);
        buf.setOffset(0);
        buf.setLength(0);
        ended = true;
        return;
             }
             nextImage++;
             int data[] = null;
             // Check the input buffer type & size.
             if (buf.getData() instanceof int[]) {
        data = (int[]) buf.getData();
             }
             // Check to see the given buffer is big enough for the frame.
             if (data == null || data.length < width * height) {
        data = new int[width * height];
        buf.setData(data);
             }
             // Bug fix from Forums ( next 3 lines).
             long time = (long) (seqNo * (1000 / frameRate) * 1000000);
             buf.setTimeStamp(time);
             buf.setSequenceNumber(seqNo++);
             java.awt.Color clr = Color.RED;
             if (nextImage > 30) {
        clr = Color.GREEN;
             }
             if (nextImage > 60) {
        clr = Color.BLUE;
             }
             // Paint all pixels the same color.
             for (int i = 0; i < width * height; i++) {
        data[i] = clr.getRGB();
             }
             buf.setOffset(0);
             buf.setLength(width * height);
             buf.setFormat(format);
             buf.setFlags(buf.getFlags() | Buffer.FLAG_KEY_FRAME);
           }
     */
 
    /**
     * Return the format of each video frame. That will be JPEG.
     */
    public Format getFormat() {
      return format;
    }
 
    public ContentDescriptor getContentDescriptor() {
      return new ContentDescriptor(ContentDescriptor.RAW);
    }
 
    public long getContentLength() {
      return 0;
    }
 
    public boolean endOfStream() {
      return ended;
    }
 
    public Object[] getControls() {
      return new Object[0];
    }
 
    public Object getControl(String type) {
      return null;
    }
  }
}
Jusque la tout va bien je peux produire du AVI ou du Quicktime non-compresse.

1) le bug : si on met la largeur de l'image a 657 (et uniquement la largeur et uniquement a cette valeur-ci) l'exportation de l'image en AVI souffre d'un decalage alors que la meme exportation en Quicktime (juste en changeant l'extension du nom du fichier destination et FileTypeDescriptor.MSVIDEO en FileTypeDescriptor.QUICKTIME) reste correcte. C'est independant de la recopie de l'image source dans un autre (dans le cas ou l'image n'a pas la taille souhaitee pour la video - j'ai teste egalement en utilisant directement les images sources) et vient donc bien de cette valeur precise. Pour toute autre largeur l'exportation est correcte.



2) Impossible de produire du MPEG en mettant l'extension qu'il faut pour le fichier destination en et choisissant FileTypeDescriptor.MPEG ; j'ai :

Cannot create the DataSink: javax.media.NoDataSinkException: Cannot find a DataSink for: com.sun.media.multiplexer.RawBufferMux$RawBufferDataSource@1de256f
Failed to create a DataSink for the given output MediaLocator: file:C:\Documents and Settings\FabriceB\Desktop\035\test.mpeg
ou

Cannot create the DataSink: javax.media.NoDataSinkException: Cannot find a DataSink for: com.sun.media.multiplexer.RawBufferMux$RawBufferDataSource@1de256f
Failed to create a DataSink for the given output MediaLocator: file:C:\Documents and Settings\FabriceB\Desktop\035\test.mpg
3) enfin pour le moment je produit du RAW donc les videos sont certes nettes mais tres grosses, est-il possible de specifier une methode de compression et/ou de recuperer une liste des codecs disponible et si oui comment les utiliser ?

4) optionnel : comment mettre des infos (createur, date, etc) dans le fichier produit ou du moins une page expliquant comment faire ?

Merci de toute aide ou indication potentielle.

PS : je n'ai pas installe le performance pack pour Windows.