使用javacv,解码socket接收的H264码流(byte[]),转为yuv处理,最后再合成转为H264
其实是一个用java实现录像的功能,还没有实现,但解码和转码已经可以。
1.maven环境,pom.xml配置
1 <properties>
2 <javacpp.version>1.4.1</javacpp.version>
3 <!-- ffmpeg版本 -->
4 <ffmpeg.version>3.4.2</ffmpeg.version>
5 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
6 <servlet.version>3.1.0</servlet.version>
7 </properties>
8 <dependency>
9 <groupId>org.bytedeco</groupId>
10 <artifactId>javacv-platform</artifactId>
11 <version>${javacpp.version}</version>
12 </dependency>
13 <dependency>
14 <groupId>org.bytedeco</groupId>
15 <artifactId>javacpp</artifactId>
16 <version>${javacpp.version}</version>
17 </dependency>
18 <dependency>
19 <groupId>org.bytedeco.javacpp-presets</groupId>
20 <artifactId>ffmpeg-platform</artifactId>
21 <version>${ffmpeg.version}-${javacpp.version}</version>
22 </dependency>
2.代码
1 package com.br.test;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.File;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.nio.ByteBuffer;
8 import org.bytedeco.javacpp.*;
9 import static org.bytedeco.javacpp.avcodec.*;
10 import static org.bytedeco.javacpp.avformat.*;
11 import static org.bytedeco.javacpp.avutil.*;
12 import static org.bytedeco.javacpp.swscale.*;
13
14 public class NewTest {
15 // Load only once formats and codecs
16 static {
17 av_register_all();
18 // avformat_network_init();
19 avcodec_register_all();
20 }
21 public int codec_id;
22 public AVCodecContext m_pCodecCtx = null; // URL中视频解码部分内容
23 public AVFrame m_pFrame = null; // 全局使用帧对象
24 public AVFrame m_pFrameRGB = null;
25 public AVCodec m_pCodec = null;
26 public AVCodecParserContext pCodecParserCtx = null;
27 public AVPacket packet = null;
28 public SwsContext m_pImageConvertCtx = null; // 构造全局对象,供sws_scale切割图片使用
29 public SwsContext img_convert_ctx = null;
30 public Integer count = 0;
31 public int count_size;
32 public BytePointer cur_ptr;
33 public FileOutputStream os;
34 private ByteArrayOutputStream myByteArrayOutputStream = new ByteArrayOutputStream();
35
36 public NewTest() {
37 System.out.println("init begin");
38
39 m_pFrame = av_frame_alloc();
40 codec_id = AV_CODEC_ID_H264;
41 m_pFrameRGB = av_frame_alloc();
42 m_pCodec = avcodec_find_decoder(codec_id);
43
44 if (m_pCodec == null) {
45 System.out.println("Codec not found\n");
46 }
47 m_pCodecCtx = avcodec_alloc_context3(m_pCodec);
48 if (m_pCodecCtx == null) {
49 System.out.println("Could not allocate video codec context\n");
50 }
51
52 pCodecParserCtx = av_parser_init(codec_id);
53 if (pCodecParserCtx == null) {
54 System.out.println("Could not allocate video parser context\n");
55 }
56
57 if (avcodec_open2(m_pCodecCtx, m_pCodec, (PointerPointer<Pointer>) null) < 0) {
58 System.out.println("Could not open codec\n");
59 }
60 img_convert_ctx = sws_getContext(1920, 1088, AV_PIX_FMT_YUV420P, 1920, 1088, AV_PIX_FMT_BGRA, SWS_FAST_BILINEAR,
61 null, null, (DoublePointer) null);
62
63 packet = new AVPacket();
64
65 packet = av_packet_alloc();
66
67 av_new_packet(packet, 30000);
68
69 int numBytes = av_image_get_buffer_size(AV_PIX_FMT_BGRA, 1920, 1088, 1);
70
71 BytePointer rgbData = new BytePointer(av_malloc(numBytes));
72
73 av_image_fill_arrays(m_pFrameRGB.data(), m_pFrameRGB.linesize(), rgbData, AV_PIX_FMT_BGRA, 1920, 1088, 1);
74
75 System.out.println("init end");
76 }
77
78 public void dec_loop(byte[] H264) {
79
80 ByteBuffer data = ByteBuffer.allocate(H264.length);
81 data.put(H264);
82 int cur_size = H264.length;
83 IntPointer pktSize = new IntPointer(packet.size());
84 BytePointer temp_bp = new BytePointer();
85 while (cur_size > 0) {
86
87 data.flip();
88 int slen = av_parser_parse2(pCodecParserCtx, m_pCodecCtx, temp_bp, pktSize, new BytePointer(data), cur_size,
89 AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);
90 packet = packet.size(pktSize.get());
91
92 data.position(slen);
93 data = data.compact();
94 cur_size -= slen;
95
96 if (pktSize.get() == 0) {
97 continue;
98 }
99 packet = packet.data(temp_bp);
100 // for(int i = 0;i <5;i++){
101 // byte b = packet.data().get(i);
102 // System.out.print(byteToHex(b)+" ");
103 // }
104 // System.out.println("------------------------------!!!!------------------"+packet.size());
105 // packet.data().asBuffer();
106
107 int asp = avcodec_send_packet(m_pCodecCtx, packet);
108
109 if (avcodec_receive_frame(m_pCodecCtx, m_pFrame) == 0) {
110 // m_pFrame.data(0);
111 // y = m_pFrame->data[0];1920*1088
112 // u = m_pFrame->data[1];1920*1088/4
113 // v = m_pFrame->data[2];1920*1088/4
114
115 System.err.println(
116 "->>> decode success " + "width :" + m_pFrame.width() + " " + "height :" + m_pFrame.height());
117
118 sws_scale(img_convert_ctx, m_pFrame.data(), m_pFrame.linesize(), 0, m_pCodecCtx.height(),
119 m_pFrameRGB.data(), m_pFrameRGB.linesize());
120 BytePointer by_bgra_data = m_pFrameRGB.data(0);
121 try {
122 // String imgName = "C:/Users/user/Desktop/test/test" + count + ".h264";
123 // saveImg(m_pFrame, imgName);
124 // count++;
125 // for (int i = 0; i < 1920 * 1088 * 4; i++) {
126 // myByteArrayOutputStream.write(by_bgra_data.get(i));
127 // }
128 // if (myByteArrayOutputStream.size() == 1920 * 1088 * 4) {
129 // File file = new
130 // File("C://Users//user//Desktop//test//success.yuv");
131 // if (!file.exists()) {
132 // file.createNewFile();
133 // }
134 // FileOutputStream fe = new FileOutputStream(file, true);
135 // fe.write(myByteArrayOutputStream.toByteArray());
136 // fe.flush();
137 // fe.close();
138 // myByteArrayOutputStream.reset();
139 // }
140 } catch (Exception e) {
141 e.printStackTrace();
142 }
143 }
144 // av_packet_unref(packet);
145 }
146 // av_packet_free(packet);
147 }
148
149 public int saveImg(AVFrame pFrame, String out_file) throws IOException {
150 AVCodec codec = null;
151 AVPacket pkt = null;
152 AVStream pAVStream = null;
153 int ret = -1;
154 AVDictionary avd = new AVDictionary(null);
155 int width = pFrame.width(), height = pFrame.height();
156 // 分配AVFormatContext对象
157 AVFormatContext pFormatCtx = avformat_alloc_context();
158 // 设置输出文件格式
159 pFormatCtx.oformat(av_guess_format("h264", null, null));
160 if (pFormatCtx.oformat() == null) {
161 return -1;
162 }
163 try {
164 // 创建并初始化一个和该url相关的AVIOContext
165 AVIOContext pb = new AVIOContext();
166 if (avio_open(pb, out_file, AVIO_FLAG_READ_WRITE) < 0) {// dont open
167 // file
168 return -1;
169 }
170 pFormatCtx.pb(pb);
171 // 构建一个新stream
172 pAVStream = avformat_new_stream(pFormatCtx, codec);
173 if (pAVStream == null) {
174 return -1;
175 }
176 int codec_id = pFormatCtx.oformat().video_codec();
177 // 设置该stream的信息
178 AVCodecContext pCodecCtx = pAVStream.codec();
179 pCodecCtx.codec_id(codec_id);
180 pCodecCtx.codec_type(AVMEDIA_TYPE_VIDEO);
181 pCodecCtx.pix_fmt(AV_PIX_FMT_YUV420P);
182 pCodecCtx.width(width);
183 pCodecCtx.height(height);
184 pCodecCtx.time_base().num(1);
185 pCodecCtx.time_base().den(25);
186 pCodecCtx.qmin(10);
187 pCodecCtx.qmax(51);
188 pCodecCtx.bit_rate(400000);
189 pCodecCtx.gop_size(12);
190 pCodecCtx.qcompress(0.6f);
191
192 if (pCodecCtx.codec_id() == AV_CODEC_ID_H264) {
193 av_dict_set(avd, "preset", "slow", 0);
194 av_dict_set(avd, "tune", "zerolatency", 0);
195 }
196
197 // Begin Output some information
198 av_dump_format(pFormatCtx, 0, out_file, 1);
199 // End Output some information
200
201 // 查找编码器
202 AVCodec pCodec = avcodec_find_encoder(pCodecCtx.codec_id());
203 if (pCodec == null) {// codec not found
204 return -1;
205 }
206 // 设置pCodecCtx的解码器为pCodec
207 if (avcodec_open2(pCodecCtx, pCodec, avd) < 0) {
208 System.err.println("Could not open codec.");
209 av_dict_free(avd);
210 return -1;
211 }
212
213 // Write Header
214 avformat_write_header(pFormatCtx, (PointerPointer<Pointer>) null);
215
216 // 给AVPacket分配足够大的空间
217 pkt = new AVPacket();
218 int yuvSize = ((width * height) / 2) * 3;
219 if (av_new_packet(pkt, yuvSize) < 0) {
220 return -1;
221 }
222
223 int[] got_picture = { 0 };
224 // encode
225
226 if (avcodec_encode_video2(pCodecCtx, pkt, pFrame, got_picture) >= 0) {
227 System.out.println("got_picture[0]:" + got_picture[0]);
228 if (got_picture[0] == 1) {
229 // flush
230 BytePointer pkt_data = pkt.data();
231 // 输出pkt数据到文件
232 for (int i = 0; i < pkt.size(); i++) {
233 myByteArrayOutputStream.write(pkt_data.get(i));
234 }
235 if (myByteArrayOutputStream.size() == pkt.size()) {
236 File file = new File("C://Users//user//Desktop//test//success.h264");
237 if (!file.exists()) {
238 file.createNewFile();
239 }
240 FileOutputStream fe = new FileOutputStream(file, true);
241 fe.write(myByteArrayOutputStream.toByteArray());
242 fe.flush();
243 fe.close();
244 myByteArrayOutputStream.reset();
245 }
246
247 if ((ret = av_write_frame(pFormatCtx, pkt)) >= 0) {
248 // Write Trailer
249 if (av_write_trailer(pFormatCtx) >= 0) {
250 System.err.println("->>> Encode Successful. pkt.size():" + pkt.size());
251 } else {
252 System.err.println("Encode failed.");
253 }
254 }
255 }
256 }
257 return ret;
258 // 结束时销毁
259 } finally {
260 if (pkt != null) {
261 av_free_packet(pkt);
262 }
263 if (pAVStream != null) {
264 avcodec_close(pAVStream.codec());
265 }
266 if (pFormatCtx != null) {
267 avio_close(pFormatCtx.pb());
268 avformat_free_context(pFormatCtx);
269 }
270 }
271 }
272
273 public static byte[] conver(ByteBuffer byteBuffer) {
274 int len = byteBuffer.limit() - byteBuffer.position();
275 byte[] bytes = new byte[len];
276
277 if (byteBuffer.isReadOnly()) {
278 return null;
279 } else {
280 byteBuffer.get(bytes);
281 }
282 return bytes;
283 }
284
285 public static String byteToHex(byte b) {
286 String hex = Integer.toHexString(b & 0xFF);
287 if (hex.length() < 2) {
288 hex = "0" + hex;
289 }
290 return hex;
291 }
292
293 public static void main(String[] args) {
294 NewTest test = new NewTest();
295 }
296
297 }
因为在网上很少有用javacv来解码的例子,只能自己踩坑前行,一点点的测试,所以可能有很多错误或可以优化的地方。
自己项目中用到,以此来记录,便于以后再遇到相同的问题。
使用javacv,解码socket接收的H264码流(byte[]),转为yuv处理,最后再合成转为H264的更多相关文章
- H264码流打包分析
转自:http://www.360doc.com/content/13/0124/08/9008018_262076786.shtml SODB 数据比特串-->最原始的编码数据 RBSP ...
- H264码流打包分析(精华)
H264码流打包分析 SODB 数据比特串-->最原始的编码数据 RBSP 原始字节序列载荷-->在SODB的后面填加了结尾比特(RBSP trailing bits 一个bit“1”)若 ...
- H264码流解析及NALU
ffmpeg 从mp4上提取H264的nalu http://blog.csdn.net/gavinr/article/details/7183499 639 /* bitstream fil ...
- H264码流中SPS PPS详解<转>
转载地址:https://zhuanlan.zhihu.com/p/27896239 1 SPS和PPS从何处而来? 2 SPS和PPS中的每个参数起什么作用? 3 如何解析SDP中包含的H.264的 ...
- RTP协议全解析(H264码流和PS流)
转自:http://blog.csdn.net/chen495810242/article/details/39207305 写在前面:RTP的解析,网上找了很多资料,但是都不全,所以我力图整理出一个 ...
- (转)RTP协议全解(H264码流和PS流)
写在前面:RTP的解析,网上找了很多资料,但是都不全,所以我力图整理出一个比较全面的解析, 其中借鉴了很多文章,我都列在了文章最后,在此表示感谢. 互联网的发展离不开大家的无私奉献,我决定从我做起,希 ...
- RTP协议全解(H264码流和PS流)
写在前面:RTP的解析,网上找了很多资料,但是都不全,所以我力图整理出一个比较全面的解析, 其中借鉴了很多文章,我都列在了文章最后,在此表示感谢. 互联网的发展离不开大家的无私奉献,我决定从我做起,希 ...
- H264编码原理以及I帧、B和P帧详解, H264码流结构分析
H264码流结构分析 http://blog.csdn.net/chenchong_219/article/details/37990541 1.码流总体结构: h264的功能分为两层,视频编码层(V ...
- 从H264码流中获取视频宽高 (SPS帧) 升级篇
之前写过 <从H264码流中获取视频宽高 (SPS帧)> . 但发现很多局限性,而且有时解出来是错误的. 所以重新去研究了. 用了 官方提供的代码库来解析. 花了点时间,从代码库里单独把解 ...
- 从H264码流中获取视频宽高 (SPS帧)
获取.h264视频宽高的方法 花了2个通宵终于搞定.(后面附上完整代码) http://write.blog.csdn.net/postedit/7852406 图像的高和宽在H264的SPS帧中.在 ...
随机推荐
- C# DataGridView中单元格Cell改变事件
DataGridView控件中的各种事件都无法直接响应Cell中内容的变化,包括KeyPress等事件,可以采用下面方法 private void dataGridViewBarcode_Editin ...
- C#实现枚举的相关操作
枚举中的Descript()描述值,以及枚举值是一种一一对应的关系.我们可以获取其描述值和枚举值,存放到字典中, 在实际的使用中我们就可以轻松的根据枚举值来获取其描述值,也可以通过枚举的描述值来获取其 ...
- java实现点选汉字验证码
package com.rd.p2p.web; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; im ...
- Maximum repetition substring(POJ - 3693)(sa(后缀数组)+st表)
The repetition number of a string is defined as the maximum number \(R\) such that the string can be ...
- spring cloud学习(四) 动态路由
Zuul的主要功能是路由和过滤器.路由功能是微服务的一部分,zuul实现了负载均衡. 1.1 新建模块zuul pom.xml <?xml version="1.0" enc ...
- 【ElasticSearch】:elasticsearch.yml配置
ElasticSearch5的elasticsearch.yml配置 注意 elasticsearch.yml中的配置,冒号和后面配置值之间有空格 cluster.name: my-applicati ...
- es6 学习小记 扩展运算符 三个点(...)
参考: es6 扩展运算符 三个点(...) 经常回顾,方能真正掌握. 一.含义 扩展运算符( spread )是三个点(...).它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列. ...
- Spring MVC前后端数据交互总结
控制器 作为控制器,大体的作用是作为V端的数据接收并且交给M层去处理,然后负责管理V的跳转.SpringMVC的作用不外乎就是如此,主要分为:接收表单或者请求的值,定义过滤器,跳转页面:其实就是ser ...
- linux(乌班图)下执行pip没有问题,执行sudo pip报错的问题
最近刚装好linux的虚拟机,在装一个套件时提示权限不足,于是添加上了 sudo 命令,结果直接报以下错误, Traceback (most recent call last): File " ...
- [Leetcode]931.下降路径最小和
题目链接 这一题目首先需要弄懂题目的意思,下降路径最小和指的是在方阵中可以从上往下行走,走过后获得的值最小,方向可以是走左下,右下,直下. 题目和传统的动态规划一样,把边界的值先初始化,然后通过状态转 ...