Download Jar. Uses 16081K of libraries. Click here for Pure Java version (4818L/27K).
1 | !7 |
2 | |
3 | // argument: mkv file to read |
4 | // output: png files |
5 | |
6 | // from https://github.com/vzhn/ffmpeg-java-samples/blob/master/src/main/java/DemuxAndDecodeH264.java |
7 | |
8 | lib 1400544 // javacpp ffmpeg examples for linux x64 |
9 | |
10 | p { DemuxAndDecodeH264.main(args); } |
11 | |
12 | import org.bytedeco.javacpp.*; |
13 | |
14 | import javax.imageio.ImageIO; |
15 | import java.awt.image.BufferedImage; |
16 | import java.awt.image.DataBufferByte; |
17 | import java.io.File; |
18 | import java.io.IOException; |
19 | import java.time.Duration; |
20 | import java.time.temporal.ChronoUnit; |
21 | |
22 | import static org.bytedeco.javacpp.avcodec.*; |
23 | import static org.bytedeco.javacpp.avformat.*; |
24 | import static org.bytedeco.javacpp.avutil.*; |
25 | import org.bytedeco.javacpp.presets.avutil; |
26 | |
27 | /** |
28 | * Read and decode h264 video from matroska (MKV) container |
29 | */ |
30 | sclass DemuxAndDecodeH264 { |
31 | /** Matroska format context */ |
32 | private AVFormatContext avfmtCtx; |
33 | |
34 | /** Matroska video stream information */ |
35 | private AVStream videoStream; |
36 | |
37 | /** matroska packet */ |
38 | private AVPacket avpacket; |
39 | |
40 | /** H264 Decoder ID */ |
41 | private AVCodec codec; |
42 | |
43 | /** H264 Decoder context */ |
44 | private AVCodecContext codecContext; |
45 | |
46 | /** yuv420 frame */ |
47 | private AVFrame yuv420Frame; |
48 | |
49 | /** RGB frame */ |
50 | private AVFrame rgbFrame; |
51 | |
52 | /** java RGB frame */ |
53 | private BufferedImage img; |
54 | |
55 | /** yuv420 to rgb converter */ |
56 | private swscale.SwsContext sws_ctx; |
57 | |
58 | /** number of frame */ |
59 | private int nframe; |
60 | |
61 | int maxFrames = 10; |
62 | |
63 | /* 1/1000 of second */ |
64 | private AVRational tb1000; |
65 | |
66 | private DemuxAndDecodeH264() { |
67 | tb1000 = new AVRational(); |
68 | tb1000.num(1); |
69 | tb1000.den(1000); |
70 | } |
71 | |
72 | public static void main(String... argv) throws IOException { |
73 | new DemuxAndDecodeH264().start(argv); |
74 | } |
75 | |
76 | private void start(String[] argv) throws IOException { |
77 | av_log_set_level(AV_LOG_VERBOSE); |
78 | |
79 | openInput(argv[0]); |
80 | findVideoStream(); |
81 | initDecoder(); |
82 | initRgbFrame(); |
83 | initYuv420Frame(); |
84 | getSwsContext(); |
85 | |
86 | avpacket = new avcodec.AVPacket(); |
87 | while ((av_read_frame(avfmtCtx, avpacket)) >= 0 && !done()) { |
88 | if (avpacket.stream_index() == videoStream.index()) { |
89 | processAVPacket(avpacket); |
90 | } |
91 | av_packet_unref(avpacket); |
92 | } |
93 | // now process delayed frames |
94 | if (!done()) processAVPacket(null); |
95 | free(); |
96 | } |
97 | |
98 | private AVFormatContext openInput(String file) throws IOException { |
99 | avfmtCtx = new AVFormatContext(null); |
100 | BytePointer filePointer = new BytePointer(file); |
101 | int r = avformat.avformat_open_input(avfmtCtx, filePointer, null, null); |
102 | filePointer.deallocate(); |
103 | if (r < 0) { |
104 | avfmtCtx.close(); |
105 | throw new IOException("avformat_open_input error: " + r); |
106 | } |
107 | return avfmtCtx; |
108 | } |
109 | |
110 | private void findVideoStream() throws IOException { |
111 | int r = avformat_find_stream_info(avfmtCtx, (PointerPointer) null); |
112 | if (r < 0) { |
113 | avformat_close_input(avfmtCtx); |
114 | avfmtCtx.close(); |
115 | throw new IOException("error: " + r); |
116 | } |
117 | |
118 | PointerPointer<AVCodec> decoderRet = new PointerPointer<>(1); |
119 | int videoStreamNumber = av_find_best_stream(avfmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, decoderRet, 0); |
120 | if (videoStreamNumber < 0) { |
121 | throw new IOException("failed to find video stream"); |
122 | } |
123 | |
124 | if (decoderRet.get(AVCodec.class).id() != AV_CODEC_ID_H264) { |
125 | throw new IOException("failed to find h264 stream"); |
126 | } |
127 | decoderRet.deallocate(); |
128 | videoStream = avfmtCtx.streams(videoStreamNumber); |
129 | } |
130 | |
131 | private void initDecoder() { |
132 | codec = avcodec_find_decoder(AV_CODEC_ID_H264); |
133 | codecContext = avcodec_alloc_context3(codec); |
134 | if((codec.capabilities() & avcodec.AV_CODEC_CAP_TRUNCATED) != 0) { |
135 | codecContext.flags(codecContext.flags() | avcodec.AV_CODEC_CAP_TRUNCATED); |
136 | } |
137 | avcodec_parameters_to_context(codecContext, videoStream.codecpar()); |
138 | if(avcodec_open2(codecContext, codec, (PointerPointer) null) < 0) { |
139 | throw new RuntimeException("Error: could not open codec.\n"); |
140 | } |
141 | } |
142 | |
143 | private void initYuv420Frame() { |
144 | yuv420Frame = av_frame_alloc(); |
145 | if (yuv420Frame == null) { |
146 | throw new RuntimeException("Could not allocate video frame\n"); |
147 | } |
148 | } |
149 | |
150 | private void initRgbFrame() { |
151 | rgbFrame = av_frame_alloc(); |
152 | rgbFrame.format(AV_PIX_FMT_BGR24); |
153 | rgbFrame.width(codecContext.width()); |
154 | rgbFrame.height(codecContext.height()); |
155 | int ret = av_image_alloc(rgbFrame.data(), |
156 | rgbFrame.linesize(), |
157 | rgbFrame.width(), |
158 | rgbFrame.height(), |
159 | rgbFrame.format(), |
160 | 1); |
161 | if (ret < 0) { |
162 | throw new RuntimeException("could not allocate buffer!"); |
163 | } |
164 | img = new BufferedImage(rgbFrame.width(), rgbFrame.height(), BufferedImage.TYPE_3BYTE_BGR); |
165 | } |
166 | |
167 | private void getSwsContext() { |
168 | sws_ctx = swscale.sws_getContext( |
169 | codecContext.width(), codecContext.height(), codecContext.pix_fmt(), |
170 | rgbFrame.width(), rgbFrame.height(), rgbFrame.format(), |
171 | 0, null, null, (DoublePointer) null); |
172 | } |
173 | |
174 | private void processAVPacket(AVPacket avpacket) throws IOException { |
175 | int ret = avcodec.avcodec_send_packet(codecContext, avpacket); |
176 | if (ret < 0) { |
177 | throw new RuntimeException("Error sending a packet for decoding\n"); |
178 | } |
179 | receiveFrames(); |
180 | } |
181 | |
182 | bool done() { ret nframe >= maxFrames; } |
183 | |
184 | private void receiveFrames() throws IOException { |
185 | int ret = 0; |
186 | while (ret >= 0 && !done()) { |
187 | ret = avcodec.avcodec_receive_frame(codecContext, yuv420Frame); |
188 | if (ret == avutil.AVERROR_EAGAIN() |
189 | || ret == org.bytedeco.javacpp.avutil.AVERROR_EOF()) { |
190 | continue; |
191 | } else |
192 | if (ret < 0) { |
193 | throw new RuntimeException("error during decoding"); |
194 | } |
195 | swscale.sws_scale(sws_ctx, yuv420Frame.data(), yuv420Frame.linesize(), 0, |
196 | yuv420Frame.height(), rgbFrame.data(), rgbFrame.linesize()); |
197 | |
198 | rgbFrame.best_effort_timestamp(yuv420Frame.best_effort_timestamp()); |
199 | processFrame(rgbFrame); |
200 | } |
201 | } |
202 | |
203 | private void processFrame(AVFrame rgbFrame) throws IOException { |
204 | DataBufferByte buffer = (DataBufferByte) img.getRaster().getDataBuffer(); |
205 | rgbFrame.data(0).get(buffer.getData()); |
206 | |
207 | long ptsMillis = av_rescale_q(rgbFrame.best_effort_timestamp(), videoStream.time_base(), tb1000); |
208 | Duration d = Duration.of(ptsMillis, ChronoUnit.MILLIS); |
209 | |
210 | String name = String.format("img_%05d_%02d-%02d-%02d-%03d.png", ++nframe, |
211 | d.toHoursPart(), |
212 | d.toMinutesPart(), |
213 | d.toSecondsPart(), |
214 | d.toMillisPart()); |
215 | ImageIO.write(img, "png", new File(name)); |
216 | } |
217 | |
218 | private void free() { |
219 | av_packet_unref(avpacket); |
220 | avcodec.avcodec_close(codecContext); |
221 | avcodec.avcodec_free_context(codecContext); |
222 | |
223 | swscale.sws_freeContext(sws_ctx); |
224 | av_frame_free(rgbFrame); |
225 | av_frame_free(yuv420Frame); |
226 | avformat.avformat_close_input(avfmtCtx); |
227 | avformat.avformat_free_context(avfmtCtx); |
228 | } |
229 | } |
Began life as a copy of #1033823
download show line numbers debug dex old transpilations
Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1033824 |
Snippet name: | JavaCPP ffpmeg Decode H264 Demo [OK on Linux x64] |
Eternal ID of this version: | #1033824/11 |
Text MD5: | e8ca5967c3c9f9a7faaeaf72a7711599 |
Transpilation MD5: | 188211def96447428b97e84f55f3d806 |
Author: | stefan |
Category: | javax / gazelle v |
Type: | JavaX source code (desktop) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-01-06 19:34:14 |
Source code size: | 7717 bytes / 229 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 232 / 981 |
Version history: | 10 change(s) |
Referenced in: | [show references] |