bytedeco / javacv

Java interface to OpenCV, FFmpeg, and more
Other
7.39k stars 1.56k forks source link

High quality gif with FfmpegFrameRecorder #1112

Open Hosseinyzr opened 5 years ago

Hosseinyzr commented 5 years ago

Hi, I'm using FfmpegFrameGrabber and FfmpegFrameRecorder to making Gif. First I extract Gif frames by grabber and convert them to bitmaps by Bitmap frameBitmap = new AndroidFrameConverter().convert(frame );

then I do some Edits on Them and convert them to Frame object again. and then make gif of them by recorder. I have tested different PixelFormat But the quality is still bad. also set videoquality to 1. .... from ffmpeg documentation I have found these commands make the Gif with high quality.. ffmpeg -i StickAround.mp4 -filter_complex "[0:v] palettegen" palette.png ffmpeg -i StickAround.mp4 -i palette.png -filter_complex "[0:v][1:v] paletteuse" prettyStickAround.gif is it possible to do this? I know it may be done by FrameFilter but I dont know how...

saudet commented 5 years ago

Check the logs to make sure the same codec gets used in both cases.

Hosseinyzr commented 5 years ago

You mean grabber and recorder? They are not the same! And I don't think it's about it. Because once I convert the final Frame objects to bitmap again and the quality was good. But i tested with the same codecs , the result was like before... Actually I think it's about ffmpeg and quality settings. And those filters I mentioned. Isn't any way to implement those filters? Could you please explain more?🙏

saudet commented 5 years ago

You're not using filters. If you're using the same codecs with the same settings, the quality will be the same.

saudet commented 5 years ago

Ah, I see what you mean, we can't set the palette as part of the settings of the codec, we need to go through filters. Sounds like issue #287. Try to do something similar to what is shown there.

Hosseinyzr commented 5 years ago

Thanks, Yes I have seen that issue before, and it get me the idea... But didn't know how exactly do this...

Hosseinyzr commented 5 years ago

I thnik palettegen needs the whole video, but we extract frame by frame... and actually I have the gif in byte[] (because of encryption/decryption). I think doing paletteuse is ok but what about palettegen...

saudet commented 5 years ago

We don't need to pass the whole video, and there's a single-frame mode too: https://ffmpeg.org/ffmpeg-filters.html#palettegen-1

Hosseinyzr commented 5 years ago

Aha. I really did'nt find the solution to generate palette. I have this exception: E/FrameFilter.Exception: avfilter_graph_parse_ptr() error -22

any body can provide me the sample code to genearte palette?

Hosseinyzr commented 5 years ago

No idea? @saudet Are you sure that is possible to implement palettegen with FfmplegFrameFilter? or we should write our own classes?

saudet commented 5 years ago

Yes, it's possible, make sure you're trying to use the right names for the input and output, you shouldn't need anything else than "in" and "out".

Hosseinyzr commented 5 years ago

@saudet Ok, I will Try again and report the result. You are one of the bests, thank you for your responses🌹

Hosseinyzr commented 5 years ago

I have tried in many ways but no success.

one of trys: filterCommand = " palettegen [myout]; [0:v][myout] paletteuse "; FFmpegFrameFilter paletteFilter = new FFmpegFrameFilter(filterCommand, w,h); paletteFilter.setPixelFormat(fFmpegFrameGrabber.getPixelFormat()); paletteFilter.setSampleFormat(fFmpegFrameGrabber.getSampleFormat()); try { paletteFilter.start(); paletteFilter.push(myFrame); Frame frame = paletteFilter.pull(); paletteFilter.stop(); paletteFilter.release(); } catch (FrameFilter.Exception e) { Logger.e("FrameFilter.Exception ", e.getMessage()); }

but it trows this exception: FrameFilter.Exception: avfilter_graph_config() error -22 ...... another thing that we cant pass the file path for output of frame filter, is it true?

saudet commented 5 years ago

I don't think they were meant to be used together like that. In any case, try to follow what the documentation says...

Hosseinyzr commented 5 years ago

I don't think it is possible to do this with ffmpegframefilter! I had tried another ways but no success!

saudet commented 5 years ago

I'm pretty sure it's possible, we just need to spend more time trying things out... It would probably help to check the debug log of FFmpeg.

Hosseinyzr commented 5 years ago

How can i get the log of this library? whats the pckagename to search the logs for it? I hava a lot of og logs in my logcat .. I don't see any special loggs for ffmpeg... ........................... I don't have problem to spending times on it but I do;nt have a clue! ............................ the command base way is simpe:

ffmpeg -i StickAround.mp4 -filter_complex "[0:v] palettegen" palette.png ffmpeg -i StickAround.mp4 -i palette.png -filter_complex "[0:v][1:v] paletteuse" prettyStickAround.gif

I think we should do this in two step, once generating palette and the use it. for generating , when I try: //pathOfImage is something like this: /storage/emulated..../a.png filterCommand = " palettegen "+pathOfImage;

I have this exception: avfilter_graph_parse_ptr() error -22

so what should I do? any other users have any idea?

Hosseinyzr commented 5 years ago

@saudet You have written this library and you know it better than me... Could you please take some time to help me? I really need it.

Hosseinyzr commented 5 years ago

@zhengxuzeng You have tried this before, Do have any Idea?

saudet commented 5 years ago

Please provide more information, such as the messages that appear on the console. If you don't see anything there, make sure to call FFmpegLogCallback.set().

Hosseinyzr commented 5 years ago

I did as You said , and these are results:

         FFmpegLogCallback.set();

        String myPath = G.APPLICATION_DIR + "/aatempPalletteGen.png";

        String filterCommand = "";
        filterCommand = " palettegen [myout]; [0:v][myout]  paletteuse ";
        FFmpegFrameFilter paletteFilter = new FFmpegFrameFilter(filterCommand, frames.get(0).getW(), frames.get(0).getH());

        try {
        paletteFilter.start();
        paletteFilter.push(myFrame);
        Frame frame = paletteFilter.pull();
        paletteFilter.stop();
        paletteFilter.release();
        FileOutputStream fileOutputStream = new FileOutputStream(new File(myPath));
        new AndroidFrameConverter().convert(frame)
        .compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);

        } catch (FrameFilter.Exception e) {
        Log.e("FrameFilter.Exception ", e.getMessage());
        } catch (FileNotFoundException e) {
        Log.e("FileNotFoundException ", e.getMessage());
        }
 //Logs:
 W/System.err: Error: Input pad "default" with type video of the filter instance "Parsed_paletteuse_1" of paletteuse not connected to any source
 E/FrameFilter.Exception: avfilter_graph_config() error -22
 I/System.out: Output #0, gif, to '':
 I/System.out:   Metadata:
 I/System.out:     encoder         :
 I/System.out: Lavf58.12.100
 I/System.out:     Stream #0:0
 I/System.out: : Video: gif, bgr8, 420x322, q=2-31, 400 kb/s
 I/System.out: ,
 I/System.out: 100 tbn

Second Try:

          FFmpegLogCallback.set();

         String myPath = G.APPLICATION_DIR + "/aatempPalletteGen.png";

         String myPalette = G.APPLICATION_DIR + "/MytempPalletteGen.png";

         String filterCommand = "";
      filterCommand = " palettegen  "+myPalette ;
         FFmpegFrameFilter paletteFilter = new FFmpegFrameFilter(filterCommand, frames.get(0).getW(), frames.get(0).getH());

         try {
             paletteFilter.start();
             paletteFilter.push(myFrame);
             Frame frame = paletteFilter.pull();
             paletteFilter.stop();
             paletteFilter.release();
             FileOutputStream fileOutputStream = new FileOutputStream(new File(myPath));
             new AndroidFrameConverter().convert(frame)
                     .compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);

         } catch (FrameFilter.Exception e) {
             Log.e("FrameFilter.Exception ", e.getMessage());
         } catch (FileNotFoundException e) {
             Log.e("FileNotFoundException ", e.getMessage());
         }

//Logs:

 W/System.err: Warning: Invalid return value 0 for stream protocol
 I/System.out: Input #0, gif, from 'java.io.ByteArrayInputStream@160d30c':
 I/System.out:   Duration:
 I/System.out: N/A
 I/System.out: , bitrate:
 I/System.out: N/A
 I/System.out:     Stream #0:0
 I/System.out: : Video: gif, bgra, 420x322
 I/System.out: ,
 I/System.out: 10 tbr,
 I/System.out: 100 tbn,
 I/System.out: 100 tbc
 W/System.err: Warning: Invalid return value 0 for stream protocol
 W/System.err: Warning: Invalid return value 0 for stream protocol
 W/System.err: Error: No such filter: 'palettegen  /storage/emulated/0/.MyToonAppDir/.nomedia/MytempPalletteGen.png'
 E/FrameFilter.Exception: avfilter_graph_parse_ptr() error -22
 I/System.out: Output #0, gif, to '':
 I/System.out:   Metadata:
 I/System.out:     encoder         :
 I/System.out: Lavf58.12.100
 I/System.out:     Stream #0:0
 I/System.out: : Video: gif, bgr8, 420x322, q=2-31, 400 kb/s
 I/System.out: ,
 I/System.out: 100 tbn

saudet commented 5 years ago

Do you have a filter command that works with ffmpeg on the command line? If so, try to start by using that!

Hosseinyzr commented 5 years ago

Do you have a filter command that works with ffmpeg on the command line? If so, try to start by using that!

I don't understand what you mean? you mean test another command?

I have tested palletgen in command line and it worked...

saudet commented 5 years ago

So try to use the same exact command with FFmpegFrameFilter. Don't add any filename or anything.

Hosseinyzr commented 5 years ago

So try to use the same exact command with FFmpegFrameFilter. Don't add any filename or anything.

this is simple command: ffmpeg -i movie.mp4 -filter_complex "[0:v] palettegen" palette.png it needs one output. and the out put will be used by paletteuse command. so what did I added and is extra?

saudet commented 5 years ago

You're adding filenames and what not to the filter command itself: That won't work! You're not doing that with ffmpeg on the command line, so don't do it with FFmpegFrameFilter either.

Hosseinyzr commented 5 years ago

What do you mean I'm not using file name in ffmpeg command line?

this is the command and it needs the out put file name and works fine:

command

Can you provide some piece of code ? or test it yourself?

saudet commented 5 years ago

That's the output filename, FFmpegFrameFilter doesn't support that. You'll need to use FFmpegFrameRecorder for that.

I already gave you a complete example above.

saudet commented 5 years ago

Actually, no, I didn't, here are examples: https://github.com/bytedeco/javacv/blob/master/platform/src/test/java/org/bytedeco/javacv/FrameFilterTest.java

Hosseinyzr commented 5 years ago

That's the output filename, FFmpegFrameFilter doesn't support that. You'll need to use FFmpegFrameRecorder for that.

I already gave you a complete example above.

Yes, I pointed that does FFmpegFrameFilter can do this filter ? and you said yes ... https://github.com/bytedeco/javacv/issues/1112#issuecomment-450372821


And I'm currently using FfmpegframeRecorder! Every thing is ok , but the problem is ffmpeg itself needs palettegen and paletuse to generate high quality gifs. and as I found from my searches this is the correct way to make high quality gifs. the pixel format and other things are effective but they aren't sufficient to make the most high quality ones.


Actually, no, I didn't, here are examples: https://github.com/bytedeco/javacv/blob/master/platform/src/test/java/org/bytedeco/javacv/FrameFilterTest.java

I see this now, and I update my javacv and ffmpeg to latest version. thank for your update.but it seems it does'nt help in this case.

saudet commented 5 years ago

FrameFilterTest works, see on Travis CI: https://travis-ci.org/bytedeco/javacv

If it doesn't work for you, you're doing something else wrong that is unrelated to your filters.

Hosseinyzr commented 5 years ago

FrameFilterTest works, see on Travis CI: https://travis-ci.org/bytedeco/javacv

If it doesn't work for you, you're doing something else wrong that is unrelated to your filters.

what was the clue in here?

I don't understand...

You mean FrameFilter can perform all of filters?! And it's not about Framefilter defect?

I really need it man... Although this repository is good and perfect but unfortunately it doesn't have good documentation. so please take some time or explain better...

saudet commented 5 years ago

Happy to help, but I won't do everything for you. Start by getting the code from FrameFilterTest to run on your machine, and then start making modifications to it for your needs.