1+ #include " iostream"
2+
3+ #include " SDL2/SDL.h"
4+ extern " C"
5+ {
6+ #include " libavdevice/avdevice.h"
7+ #include " libavformat/avformat.h"
8+ #include " libavcodec/avcodec.h"
9+ #include " libavutil/time.h"
10+ #include " libswscale/swscale.h"
11+
12+ #include < libavutil/imgutils.h>
13+
14+ struct SDL_Info
15+ {
16+ // SDL---------------------------
17+ SDL_Window *screen;
18+ SDL_Renderer *sdlRenderer;
19+ SDL_Texture *sdlTexture;
20+ SDL_Rect sdlRect;
21+ };
22+
23+ static int encode (AVCodecContext *enc_ctx, AVPacket *pkt, AVFrame *frame, AVFormatContext *fmt, FILE *f)
24+ {
25+ int ret = 0 ;
26+ if (frame)
27+ {
28+ std::cout << " frame->linesize[0] = " << frame->linesize [0 ] << std::endl;
29+ std::cout << " frame->linesize[1] = " << frame->linesize [1 ] << std::endl;
30+ std::cout << " frame->linesize[2] = " << frame->linesize [2 ] << std::endl;
31+ }
32+
33+ ret = avcodec_send_frame (enc_ctx, frame);
34+ if (ret < 0 )
35+ {
36+ std::cout << " avcodec_send_frame error,ret=" << ret << std::endl;
37+ return -1 ;
38+ }
39+ while (ret >= 0 )
40+ {
41+ ret = avcodec_receive_packet (enc_ctx, pkt);
42+ if (ret == AVERROR (EAGAIN) || ret == AVERROR_EOF)
43+ {
44+ return 0 ;
45+ }
46+ else if (ret < 0 )
47+ {
48+ std::cout << " avcodec_receive_packet error,ret=" << ret << std::endl;
49+ return -2 ;
50+ }
51+ fwrite (pkt->data , 1 , pkt->size , f);
52+ pkt->stream_index = 0 ;
53+ av_interleaved_write_frame (fmt, pkt);
54+ av_packet_unref (pkt);
55+ }
56+ return 0 ;
57+ }
58+ int rtmp_video (std::string flv)
59+ {
60+ int ret = 0 ;
61+ int video_index = -1 ;
62+ int64_t start_time = 0 ;
63+ int dst_width = 640 , dst_height = 480 ;
64+
65+ AVFormatContext *fmt_ctx = NULL ;
66+ AVInputFormat *fmt = NULL ;
67+
68+ AVFormatContext *ofmt_ctx = NULL ;
69+ AVStream *istream = NULL , *ostream = NULL ;
70+
71+ AVPacket *pkt = NULL ;
72+
73+ FILE *f_out = NULL ;
74+
75+ AVFrame *frame = NULL , *yuv_frame = NULL ;
76+ struct SwsContext *sws_ctx = NULL ;
77+ struct SDL_Info display;
78+
79+ const char *rtmp_server = " rtmp://106.13.42.53/liupeng/video" ;
80+ AVCodecContext *enc_ctx = NULL ;
81+ AVCodec *codec = NULL ;
82+
83+ f_out = fopen (flv.data (), " wb+" );
84+ pkt = av_packet_alloc ();
85+ frame = av_frame_alloc ();
86+ yuv_frame = av_frame_alloc ();
87+
88+ // 1. 注册所有输入/输出设备,必须步骤
89+ avdevice_register_all ();
90+
91+ // 2. find input format
92+ fmt = av_find_input_format (" v4l2" );
93+ if (!fmt)
94+ {
95+ std::cout << " find input format error" << std::endl;
96+ return -1 ;
97+ }
98+
99+ // 3. 打开摄像头
100+ ret = avformat_open_input (&fmt_ctx, " /dev/video0" , fmt, NULL );
101+ if (ret < 0 )
102+ {
103+ std::cout << " avformat_open_input error,ret=" << ret << std::endl;
104+ return -2 ;
105+ }
106+
107+ // 4. 查找流信息
108+ ret = avformat_find_stream_info (fmt_ctx, NULL );
109+ if (ret < 0 )
110+ {
111+ std::cout << " avformat_find_stream_info error,ret=" << ret << std::endl;
112+ return -3 ;
113+ }
114+ av_dump_format (fmt_ctx, 0 , " /dev/video0" , 0 );
115+
116+ // 为输出文件分配avformat_context
117+ avformat_alloc_output_context2 (&ofmt_ctx, NULL , " flv" , rtmp_server);
118+ if (!ofmt_ctx)
119+ {
120+ std::cout << " avformat_alloc_output_context2 for output file error,ret=" << std::endl;
121+ return -4 ;
122+ }
123+ // 打开输出文件
124+ ret = avio_open (&ofmt_ctx->pb , rtmp_server, AVIO_FLAG_WRITE);
125+ if (ret < 0 )
126+ {
127+ std::cout << " open output file error,ret=" << ret << std::endl;
128+ return -5 ;
129+ }
130+
131+ codec = avcodec_find_encoder_by_name (" h264_nvenc" );
132+ if (!codec)
133+ {
134+ std::cout << " avcodec_find_encoder_by_name h264_nvenc fail" << std::endl;
135+ return -7 ;
136+ }
137+
138+ enc_ctx = avcodec_alloc_context3 (codec);
139+ if (!enc_ctx)
140+ {
141+ std::cout << " avcodec_alloc_context3 fail" << std::endl;
142+ return -8 ;
143+ }
144+ enc_ctx->width = dst_width;
145+ enc_ctx->height = dst_height;
146+ enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
147+ enc_ctx->framerate = {25 , 1 };
148+ enc_ctx->time_base = {1 , 25 };
149+ enc_ctx->bit_rate = 4000000 ;
150+ // 5. 获取视频流索引
151+ for (size_t i = 0 ; i < fmt_ctx->nb_streams ; i++)
152+ {
153+ istream = fmt_ctx->streams [i];
154+ ostream = avformat_new_stream (ofmt_ctx, NULL );
155+ if (istream->codecpar ->codec_type == AVMEDIA_TYPE_VIDEO)
156+ {
157+ video_index = i;
158+ avcodec_parameters_from_context (ostream->codecpar , enc_ctx);
159+ break ;
160+ }
161+ }
162+
163+ if (video_index == -1 )
164+ {
165+ std::cout << " can not find video index" << std::endl;
166+ return -9 ;
167+ }
168+ std::cout << " -----------------------------------\n "
169+ << std::endl;
170+ av_dump_format (ofmt_ctx, 0 , rtmp_server, 1 );
171+ // 写文件头
172+ ret = avformat_write_header (ofmt_ctx, NULL );
173+ if (ret < 0 )
174+ {
175+ std::cout << " avformat_write_header to file error,ret=" << ret << std::endl;
176+ return -6 ;
177+ }
178+ // 8. 打开解码器
179+ ret = avcodec_open2 (enc_ctx, codec, NULL );
180+ if (ret < 0 )
181+ {
182+ std::cout << " avcodec_open2 fail,ret=" << ret << std::endl;
183+ return -10 ;
184+ }
185+
186+ // 9. 初始化转换器
187+ sws_ctx = sws_getContext (istream->codecpar ->width , istream->codecpar ->height , AVPixelFormat (istream->codecpar ->format ), dst_width, dst_height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL , NULL , NULL );
188+ if (!sws_ctx)
189+ {
190+ std::cout << " sws_getContext fail,ret=" << ret << std::endl;
191+ return -11 ;
192+ }
193+
194+ yuv_frame->width = dst_width;
195+ yuv_frame->height = dst_height;
196+ yuv_frame->format = AV_PIX_FMT_YUV420P;
197+ av_frame_get_buffer (yuv_frame, 0 );
198+
199+ frame->width = istream->codecpar ->width ;
200+ frame->height = istream->codecpar ->height ;
201+ frame->format = istream->codecpar ->format ;
202+ av_frame_get_buffer (frame, 0 );
203+
204+ // SDL初始化
205+ SDL_Init (SDL_INIT_VIDEO | SDL_INIT_AUDIO);
206+ display.screen = SDL_CreateWindow (" get yuv data from webcam" , SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
207+ dst_width, dst_height,
208+ SDL_WINDOW_OPENGL);
209+ if (!display.screen )
210+ {
211+ std::cout << " sdl create window fail" << std::endl;
212+ return -9 ;
213+ }
214+
215+ display.sdlRenderer = SDL_CreateRenderer (display.screen , -1 , 0 );
216+ display.sdlTexture = SDL_CreateTexture (display.sdlRenderer , SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, dst_width, dst_height);
217+
218+ display.sdlRect .x = 0 ;
219+ display.sdlRect .y = 0 ;
220+ display.sdlRect .w = dst_width;
221+ display.sdlRect .h = dst_height;
222+
223+ AVPacket *opkt = NULL ;
224+ opkt = av_packet_alloc ();
225+ start_time = av_gettime ();
226+
227+ while (true )
228+ {
229+ ret = av_read_frame (fmt_ctx, pkt);
230+ if (ret < 0 )
231+ {
232+ break ;
233+ }
234+ memcpy (frame->data [0 ], pkt->data , pkt->size );
235+ sws_scale (sws_ctx, frame->data , frame->linesize , 0 , frame->height , yuv_frame->data , yuv_frame->linesize );
236+ encode (enc_ctx, opkt, yuv_frame, ofmt_ctx, f_out);
237+ // 设置纹理数据
238+ SDL_UpdateYUVTexture (display.sdlTexture , &display.sdlRect ,
239+ yuv_frame->data [0 ], yuv_frame->linesize [0 ],
240+ yuv_frame->data [1 ], yuv_frame->linesize [1 ],
241+ yuv_frame->data [2 ], yuv_frame->linesize [2 ]);
242+ // SDL_RenderClear(sdlRenderer);
243+ // 纹理复制给渲染器
244+ SDL_RenderCopy (display.sdlRenderer , display.sdlTexture , NULL , &display.sdlRect );
245+ // 显示
246+ SDL_RenderPresent (display.sdlRenderer );
247+ if (av_gettime () - start_time >= 30000000 )
248+ {
249+ break ;
250+ }
251+ AVRational time_base = istream->time_base ;
252+ AVRational time_base_q = {1 , AV_TIME_BASE};
253+ int64_t pts_time = av_rescale_q (pkt->pts , time_base, time_base_q);
254+ int64_t now_time = av_gettime () - start_time;
255+ if (pts_time > now_time)
256+ av_usleep (pts_time - now_time);
257+
258+ std::cout << " pts_time=" << pts_time << std::endl;
259+ std::cout << " pkt->dts=" << pkt->dts << std::endl;
260+ std::cout << " pkt->pts=" << pkt->pts << std::endl;
261+ std::cout << " av_gettime() - start_time=" << av_gettime () - start_time << std::endl;
262+ std::cout << " istream->time_base.den=" << istream->time_base .den << std::endl;
263+ std::cout << " istream->time_base.num=" << istream->time_base .num << std::endl;
264+ }
265+
266+ encode (enc_ctx, opkt, NULL , ofmt_ctx, f_out);
267+ av_write_trailer (ofmt_ctx);
268+
269+ sws_freeContext (sws_ctx);
270+
271+ av_packet_free (&pkt);
272+ av_frame_free (&frame);
273+ av_frame_free (&yuv_frame);
274+ avcodec_free_context (&enc_ctx);
275+ avformat_close_input (&fmt_ctx);
276+
277+ avio_close (ofmt_ctx->pb );
278+ avformat_free_context (ofmt_ctx);
279+ fclose (f_out);
280+
281+ SDL_Quit ();
282+ return 0 ;
283+ }
284+ }
0 commit comments