博客园第一篇——SDL2+FFmpeg 制作简单播放器&同步
SDL 2.0.3; FFmpeg 20140402 shared;
部分来自http://blog.csdn.net/jiqiujia/article/details/22449131的第二个例子,修正了一些问题。
原来的代码在第555行,SDL_Delay(50) 应该替换为 av_free_packet(&packet),这样既解决了迟滞问题还解决了一个坑爹的内存泄露问题。(我才不会告诉你我播放一个稍微大点的视频在8秒之后 2 GB 内存就被吃掉了呢!)
https://github.com/phamquy/FFmpeg-tutorial-samples/blob/master/tutorial06.c也有一些帮助。
功能:
1、简单播放器;
2、音频重采样,大部分视频的音频部分都播放正常(也有少部分仍不正常的);
3、简单的同步。当前以音频流为基准,对于比较小(<120 MB)的 .mpg/.flv 文件还正常,其他的有各种问题,包括视频仍然过快,或者严重丢帧(建议打开控制台自己看)。
下面上代码。部分地方有注释。
1 #define _CRT_SECURE_NO_WARNINGS 2 3 extern "C"{ 4 #include "libavutil/opt.h" 5 #include "libavcodec/avcodec.h" 6 #include "libavformat/avformat.h" 7 #include "libswscale/swscale.h" 8 #include "libswresample/swresample.h" 9 } 10 #include "SDL.h" 11 #include "SDL_image.h" 12 #include "SDL_thread.h" 13 14 #include <iostream> 15 #include <queue> 16 using namespace std; 17 18 #pragma warning(disable: 4996) 19 20 #pragma comment(lib,"avutil.lib") 21 #pragma comment(lib,"avcodec.lib") 22 #pragma comment(lib,"avformat.lib") 23 #pragma comment(lib,"swscale.lib") 24 #pragma comment(lib,"swresample.lib") 25 26 #pragma comment(lib,"sdl2.lib") 27 28 #define SDL_AUDIO_BUFFER_SIZE (1152) 29 #define AVCODEC_MAX_AUDIO_FRAME_SIZE (192000) 30 31 #if (defined main && defined __MINGW__) 32 #undef main 33 #endif 34 35 static Uint8 *audio_chunk; 36 static Uint32 audio_len; 37 static Uint8 *audio_pos; 38 39 static int64_t audio_pts = 0; 40 static int64_t audio_dts = 0; 41 static int64_t video_pts = 0; 42 static int64_t video_dts = 0; 43 44 static AVFrame *g_pFrameYUV; 45 46 SDL_Thread *g_pVideoThread; 47 SDL_mutex *g_pVideoMutex; 48 49 static int quit = 0; 50 static int video_quit = 0; 51 52 typedef struct video_thread_params 53 { 54 SwsContext *sws_context; 55 SDL_Texture *texture; 56 SDL_Renderer *renderer; 57 AVCodecContext *vid_codec_context; 58 SDL_mutex *video_mutex; 59 } 60 video_thread_params; 61 62 int video_thread_proc(void *data); 63 64 void fill_audio(void *udata, Uint8 *stream, int len){ 65 if (audio_len == 0) 66 return; 67 len = (len > audio_len ? audio_len : len); 68 SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME); 69 audio_pos += len; 70 audio_len -= len; 71 } 72 73 int AudioResampling(AVCodecContext * audio_dec_ctx, 74 AVFrame * pAudioDecodeFrame, 75 int out_sample_fmt, 76 int out_channels, 77 int out_sample_rate, 78 uint8_t* out_buf) 79 { 80 SwrContext * swr_ctx = NULL; 81 int data_size = 0; 82 int ret = 0; 83 int64_t src_ch_layout = audio_dec_ctx->channel_layout; 84 int64_t dst_ch_layout = AV_CH_LAYOUT_STEREO; 85 int dst_nb_channels = 0; 86 int dst_linesize = 0; 87 int src_nb_samples = 0; 88 int dst_nb_samples = 0; 89 int max_dst_nb_samples = 0; 90 uint8_t **dst_data = NULL; 91 int resampled_data_size = 0; 92 93 swr_ctx = swr_alloc(); 94 if (!swr_ctx) 95 { 96 printf("swr_alloc error \n"); 97 return -1; 98 } 99 100 src_ch_layout = (audio_dec_ctx->channels == 101 av_get_channel_layout_nb_channels(audio_dec_ctx->channel_layout)) ? 102 audio_dec_ctx->channel_layout : 103 av_get_default_channel_layout(audio_dec_ctx->channels); 104 105 if (out_channels == 1) 106 { 107 dst_ch_layout = AV_CH_LAYOUT_MONO; 108 //printf("dst_ch_layout: AV_CH_LAYOUT_MONO\n"); 109 } 110 else if (out_channels == 2) 111 { 112 dst_ch_layout = AV_CH_LAYOUT_STEREO; 113 //printf("dst_ch_layout: AV_CH_LAYOUT_STEREO\n"); 114 } 115 else 116 { 117 dst_ch_layout = AV_CH_LAYOUT_SURROUND; 118 //printf("dst_ch_layout: AV_CH_LAYOUT_SURROUND\n"); 119 } 120 121 if (src_ch_layout <= 0) 122 { 123 printf("src_ch_layout error \n"); 124 return -1; 125 } 126 127 src_nb_samples = pAudioDecodeFrame->nb_samples; 128 if (src_nb_samples <= 0) 129 { 130 printf("src_nb_samples error \n"); 131 return -1; 132 } 133 134 av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, 0); 135 av_opt_set_int(swr_ctx, "in_sample_rate", audio_dec_ctx->sample_rate, 0); 136 av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", audio_dec_ctx->sample_fmt, 0); 137 138 av_opt_set_int(swr_ctx, "out_channel_layout", dst_ch_layout, 0); 139 av_opt_set_int(swr_ctx, "out_sample_rate", out_sample_rate, 0); 140 av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", (AVSampleFormat)out_sample_fmt, 0); 141 142 if ((ret = swr_init(swr_ctx)) < 0) { 143 printf("Failed to initialize the resampling context\n"); 144 return -1; 145 } 146 147 max_dst_nb_samples = dst_nb_samples = av_rescale_rnd(src_nb_samples, 148 out_sample_rate, audio_dec_ctx->sample_rate, AV_ROUND_UP); 149 if (max_dst_nb_samples <= 0) 150 { 151 printf("av_rescale_rnd error \n"); 152 return -1; 153 } 154 155 dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout); 156 ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize, dst_nb_channels, 157 dst_nb_samples, (AVSampleFormat)out_sample_fmt, 0); 158 if (ret < 0) 159 { 160 printf("av_samples_alloc_array_and_samples error \n"); 161 return -1; 162 } 163 164 165 dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, audio_dec_ctx->sample_rate) + 166 src_nb_samples, out_sample_rate, audio_dec_ctx->sample_rate, AV_ROUND_UP); 167 if (dst_nb_samples <= 0) 168 { 169 printf("av_rescale_rnd error \n"); 170 return -1; 171 } 172 if (dst_nb_samples > max_dst_nb_samples) 173 { 174 av_free(dst_data[0]); 175 ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels, 176 dst_nb_samples, (AVSampleFormat)out_sample_fmt, 1); 177 max_dst_nb_samples = dst_nb_samples; 178 } 179 180 if (swr_ctx) 181 { 182 ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, 183 (const uint8_t **)pAudioDecodeFrame->data, pAudioDecodeFrame->nb_samples); 184 if (ret < 0) 185 { 186 printf("swr_convert error \n"); 187 return -1; 188 } 189 190 resampled_data_size = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels, 191 ret, (AVSampleFormat)out_sample_fmt, 1); 192 if (resampled_data_size < 0) 193 { 194 printf("av_samples_get_buffer_size error \n"); 195 return -1; 196 } 197 } 198 else 199 { 200 printf("swr_ctx null error \n"); 201 return -1; 202 } 203 204 memcpy(out_buf, dst_data[0], resampled_data_size); 205 206 if (dst_data) 207 { 208 av_freep(&dst_data[0]); 209 } 210 av_freep(&dst_data); 211 dst_data = NULL; 212 213 if (swr_ctx) 214 { 215 swr_free(&swr_ctx); 216 } 217 return resampled_data_size; 218 } 219 220 //创建一个全局的结构体变量以便于我们从文件中得到的声音包有地方存 221 //放同时也保证SDL中的声音回调函数audio_callback 能从这个地方得到声音数据 222 typedef struct PacketQueue{ 223 AVPacketList *first_pkt, *last_pkt; 224 int nb_packets; 225 int size; 226 SDL_mutex *mutex;//因为SDL 是在一个独立的线程中来进行音频处理的。如果我们没有正确的锁定这个队列,我们有可能把数据搞乱。 227 SDL_cond *cond; 228 }PacketQueue; 229 230 PacketQueue audioq; 231 PacketQueue videoq; 232 queue<AVFrame *> frameq; 233 234 void packet_queue_init(PacketQueue *pq){ 235 memset(pq, 0, sizeof(PacketQueue)); 236 pq->mutex = SDL_CreateMutex(); 237 pq->cond = SDL_CreateCond(); 238 } 239 240 int packet_queue_put(PacketQueue *q, AVPacket *pkt){ 241 AVPacketList *pkt1; 242 /*( 243 if (av_dup_packet(pkt) < 0){ 244 printf("error"); 245 return -1; 246 } 247 */ 248 249 pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); 250 if (!pkt1){ 251 printf("error"); 252 return -1; 253 } 254 255 av_copy_packet(&pkt1->pkt, pkt); 256 av_free_packet(pkt); 257 pkt1->next = NULL; 258 259 //函数SDL_LockMutex()锁定队列的互斥量以便于我们向队列中添加东西,然后函 260 //数SDL_CondSignal()通过我们的条件变量为一个接 收函数(如果它在等待)发 261 //出一个信号来告诉它现在已经有数据了,接着就会解锁互斥量并让队列可以自由 262 //访问。 263 SDL_LockMutex(q->mutex); 264 265 if (!q->last_pkt)//队列为空 266 q->first_pkt = pkt1; 267 else//队列不为空 268 q->last_pkt->next = pkt1; 269 q->last_pkt = pkt1; 270 q->nb_packets++; 271 q->size += pkt1->pkt.size; 272 SDL_CondSignal(q->cond); 273 274 SDL_UnlockMutex(q->mutex); 275 276 return 0; 277 } 278 279 int decode_interrupt_cb(void){ 280 return quit; 281 } 282 283 static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block){ 284 AVPacketList *pkt1; 285 int ret; 286 287 SDL_LockMutex(q->mutex); 288 289 for (;;){ 290 if (quit){ 291 ret = -1; 292 break; 293 } 294 295 pkt1 = q->first_pkt; 296 if (pkt1){ 297 q->first_pkt = pkt1->next; 298 if (!q->first_pkt) 299 q->last_pkt = NULL; 300 q->nb_packets--; 301 q->size -= pkt1->pkt.size; 302 //*pkt = pkt1->pkt; 303 av_copy_packet(pkt, &pkt1->pkt); 304 av_free_packet(&pkt1->pkt); 305 av_free(pkt1); 306 ret = 1; 307 break; 308 } 309 else if (!block){ 310 ret = 0; 311 break; 312 } 313 else{ 314 SDL_CondWait(q->cond, q->mutex); 315 } 316 } 317 SDL_UnlockMutex(q->mutex); 318 return ret; 319 } 320 321 int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size){ 322 static AVPacket pkt; 323 static uint8_t *audio_pkt_data = NULL; 324 static int audio_pkt_size = 0; 325 326 int len1, data_size, ret = 0; 327 328 static AVFrame *pFrame; 329 pFrame = av_frame_alloc(); 330 331 /*if (packet_queue_get(&audioq, &pkt, 1) < 0){//从这里开始,取得main线程放入队列的包 332 printf("error, can't get packet from the queue"); 333 return -1; 334 } 335 336 len1 = avcodec_decode_audio4(aCodecCtx, pFrame, &ret, &pkt); 337 if (len1 < 0) 338 return -1; 339 340 return AudioResampling(aCodecCtx, pFrame, AV_SAMPLE_FMT_S16, 2, 44100, audio_buf);*/ 341 for (;;){ 342 while (audio_pkt_size > 0){ 343 data_size = buf_size; 344 len1 = avcodec_decode_audio4(aCodecCtx, pFrame, &ret, &pkt); 345 346 //len1 = avcodec_decode_audio3(aCodecCtx, (int16_t *)audio_buf, 347 // &data_size, &pkt); 348 if (len1 < 0){//if error, skip frame 349 printf("error\n"); 350 audio_pkt_size = 0; 351 break; 352 } 353 data_size = AudioResampling(aCodecCtx, pFrame, AV_SAMPLE_FMT_S16, 2, 44100, audio_buf); 354 audio_pkt_data += len1; 355 audio_pkt_size -= len1; 356 if (data_size <= 0)//No data yet, get more frames 357 continue; 358 return data_size; 359 } 360 //if (pkt.data) 361 av_free_packet(&pkt); 362 if (quit) 363 return -1; 364 if (packet_queue_get(&audioq, &pkt, 1) < 0){//从这里开始,取得main线程放入队列的包 365 printf("error, can't get packet from the queue"); 366 return -1; 367 } 368 369 //SDL_LockMutex(g_pVideoMutex); 370 audio_pts = pkt.pts; 371 audio_dts = pkt.dts; 372 //SDL_UnlockMutex(g_pVideoMutex); 373 374 audio_pkt_data = pkt.data; 375 audio_pkt_size = pkt.size; 376 } 377 } 378 //声音回调函数 379 //userdata是输入,stream是输出,len是输入,len的值一般为4096(调试中发现的), 380 //audio_callback函数的功能是调用audio_decode_frame函数,把解码后数据块audio_buf追加在stream的后面, 381 //通过SDL库对audio_callback的不断调用,不断解码数据,然后放到stream的末尾, 382 //SDL库认为stream中数据够播放一帧音频了,就播放它, 383 //第三个参数len是向stream中写数据的内存分配尺度,是分配给audio_callback函数写入缓存大小。 384 void audio_callback(void *userdata, Uint8 *stream, int len){ 385 //SDL_memset(stream, 0, len); 386 AVCodecContext *aCodecCtx = (AVCodecContext*)userdata; 387 int len1, audio_size; 388 389 //audio_buf 的大小为 1.5 倍的声音帧的大 小以便于有一个比较好的缓冲 390 static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]; 391 static unsigned int audio_buf_size = 0; 392 static unsigned int audio_buf_index = 0; 393 394 while (len > 0){ 395 if (audio_buf_index >= audio_buf_size){//already send all our data, get more 396 audio_size = audio_decode_frame(aCodecCtx, audio_buf, sizeof(audio_buf)); 397 if (audio_size < 0){//error, output silence 398 printf("error, output silence\n"); 399 audio_buf_size = SDL_AUDIO_BUFFER_SIZE; 400 memset(audio_buf, 0, audio_buf_size); 401 } 402 else 403 audio_buf_size = audio_size; 404 audio_buf_index = 0; 405 } 406 len1 = audio_buf_size - audio_buf_index; 407 if (len1>len){ 408 len1 = len; 409 } 410 memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1); 411 len -= len1; 412 stream += len1; 413 audio_buf_index += len1; 414 } 415 } 416 417 // 视频流处理线程 418 // 这里对齐是以音频流为准的 419 // 所以如果音频流(相对于视频流)过于稀疏的话,就会失去作用,效果不好 420 421 int video_thread_proc(void *data) 422 { 423 video_thread_params *params = (video_thread_params *)data; 424 AVFrame *pFrame = NULL; 425 AVFrame *pNextFrame = NULL; 426 AVPacket packet = {0}; 427 AVPacket nextpacket; 428 429 // 注意,以下代码比较的都是 DTS(解压缩时间戳)而不是 PTS(显示时间戳)!!! 430 // 实际视频显示是以 PTS 为准的,但是 PTS 大多没有规律(只要是在解压之后就好),难以处理 431 // 所幸在相当一部分视频中 DTS 与 PTS 相差较小,所以这里就用 DTS 了 432 433 while (!video_quit) 434 { 435 while (!frameq.empty()) 436 { 437 if (pFrame == NULL) 438 { 439 SDL_LockMutex(params->video_mutex); 440 441 // 这里采用了“连续读取”的方法,也即如果下一帧的 DTS 小于当前基准(目前采用音频流),则循环直至找到第一个 DTS 比基准大的 442 // 然后播放小于基准而且离基准最近的帧,并缓存下一帧 443 // 由于处理时间较长,而且使用了互斥体(看,音频部分也用到了),所以可能有极少数的音频帧有异常(噪声啊之类的) 444 // 当然也可以将这一段注释掉,采用 pFrame = frameq.count(); frameq.pop();,同时解除 if (packet_queue_get()) 的注释(和下面的一对大括号) 445 // 但是无跳过的方法会导致视频严重滞后 446 447 if (pNextFrame != NULL) 448 { 449 pFrame = pNextFrame; 450 SDL_memcpy(&packet, &nextpacket, sizeof(AVPacket)); 451 pNextFrame = NULL; 452 } 453 else 454 { 455 pFrame = frameq.front(); 456 frameq.pop(); 457 } 458 while (!frameq.empty()) 459 { 460 pNextFrame = frameq.front(); 461 frameq.pop(); 462 packet_queue_get(&videoq, &nextpacket, 1); 463 464 if (nextpacket.dts <= audio_dts) 465 { 466 av_free_packet(&packet); 467 av_frame_free(&pFrame); 468 SDL_memcpy(&packet, &nextpacket, sizeof(AVPacket)); 469 pFrame = pNextFrame; 470 pNextFrame = NULL; 471 } 472 else 473 { 474 break; 475 } 476 } 477 478 479 //pFrame = frameq.front(); 480 //frameq.pop(); 481 482 483 //cout << "vdts: " << packet.dts << " adts: " << audio_dts << endl; 484 485 //if (packet_queue_get(&videoq, &packet, 1) >= 0) 486 //{ 487 sws_scale(params->sws_context, (const uint8_t* const*)pFrame->data, 488 pFrame->linesize, 0, params->vid_codec_context->height, g_pFrameYUV->data, g_pFrameYUV->linesize); 489 490 SDL_UpdateYUVTexture(params->texture, NULL, g_pFrameYUV->data[0], g_pFrameYUV->linesize[0], 491 g_pFrameYUV->data[1], g_pFrameYUV->linesize[1], g_pFrameYUV->data[2], g_pFrameYUV->linesize[2]); 492 493 //SDL_RenderClear(params->renderer); 494 SDL_RenderCopy(params->renderer, params->texture, NULL, NULL); 495 SDL_RenderPresent(params->renderer); 496 // 可以使用 av_frame_clone() + 队列 实现“远程”dts 读取 497 //if (params->vid_codec_context->refcounted_frames) 498 //{ 499 // av_frame_unref(pFrame); 500 //} 501 //} 502 503 //cout << "--------------------------------------------------" << endl; 504 //cout << "vidpts: " << packet.pts << " audpts: " << audio_pts << endl; 505 //cout << "viddts: " << packet.dts << " auddts: " << audio_dts << endl; 506 507 SDL_UnlockMutex(params->video_mutex); 508 } 509 else 510 { 511 //cout << "vdts: " << packet.dts << " adts: " << audio_dts << endl; 512 513 // 如果当前帧应该用被缓存的帧,那就用,不要重新读取了 514 515 sws_scale(params->sws_context, (const uint8_t* const*)pFrame->data, 516 pFrame->linesize, 0, params->vid_codec_context->height, g_pFrameYUV->data, g_pFrameYUV->linesize); 517 518 SDL_UpdateYUVTexture(params->texture, NULL, g_pFrameYUV->data[0], g_pFrameYUV->linesize[0], 519 g_pFrameYUV->data[1], g_pFrameYUV->linesize[1], g_pFrameYUV->data[2], g_pFrameYUV->linesize[2]); 520 521 //SDL_RenderClear(params->renderer); 522 SDL_RenderCopy(params->renderer, params->texture, NULL, NULL); 523 SDL_RenderPresent(params->renderer); 524 // 可以使用 av_frame_clone() + 队列 实现“远程”dts 读取 525 if (params->vid_codec_context->refcounted_frames) 526 { 527 av_frame_unref(pFrame); 528 } 529 530 // 如果该帧是在音频帧之前的,那就销毁它,和数据包 531 if (packet.dts <= audio_dts) 532 { 533 av_frame_free(&pFrame); 534 av_free_packet(&packet); 535 pFrame = NULL; 536 } 537 } 538 539 } 540 } 541 542 return 0; 543 } 544 545 int main(int argc, char *argv[]) 546 { 547 av_register_all(); //注册了所有的文件格式和编解码的库,它们将被自动的使用在被打开的合适格式的文件上 548 AVFormatContext *pFormatCtx; 549 pFormatCtx = avformat_alloc_context(); 550 551 if (argc < 2) return 0; 552 //Open an input stream and read the header 553 if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0){ 554 printf("Can't open the file\n"); 555 return -1; 556 } 557 //Retrieve stream information 558 if (avformat_find_stream_info(pFormatCtx, NULL) < 0){ 559 printf("Couldn't find stream information.\n"); 560 return -1; 561 } 562 563 //output file information 564 cout << "文件信息----------------------------------" << endl; 565 av_dump_format(pFormatCtx, 0, argv[1], 0); 566 cout << "--------------------------------------------" << endl; 567 568 int i, videoIndex, audioIndex; 569 570 //Find the first video stream 571 videoIndex = -1; 572 audioIndex = -1; 573 for (i = 0; i < pFormatCtx->nb_streams; i++){//视音频流的个数 574 if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO 575 && videoIndex < 0){ 576 videoIndex = i; 577 } 578 if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO 579 && audioIndex < 0) 580 audioIndex = i; 581 } 582 583 if (videoIndex == -1) 584 return -1; 585 if (audioIndex == -1) 586 return -1; 587 588 AVCodecContext *pCodecCtx, *paCodecCtx; 589 AVCodec *pCodec, *paCodec; 590 //Get a pointer to the codec context for the video stream 591 //流中关于编解码器的信息就是被我们叫做"codec context"(编解码器上下文) 592 //的东西。这里面包含了流中所使用的关于编解码器的所有信 593 pCodecCtx = pFormatCtx->streams[videoIndex]->codec; 594 // 帧引用:打开,见 AVFrame 的注释 595 pCodecCtx->refcounted_frames = 1; 596 paCodecCtx = pFormatCtx->streams[audioIndex]->codec; 597 //Find the decoder for the video stream 598 pCodec = avcodec_find_decoder(pCodecCtx->codec_id); 599 paCodec = avcodec_find_decoder(paCodecCtx->codec_id); 600 601 if (pCodec == NULL || paCodecCtx == NULL){ 602 printf("Unsupported codec!\n"); 603 return -1; 604 } 605 //Open codec 606 if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){ 607 printf("Could not open video codec.\n"); 608 return -1; 609 } 610 if (avcodec_open2(paCodecCtx, paCodec, NULL) < 0){ 611 printf("Could not open audio codec.\n"); 612 return -1; 613 } 614 615 //--------------------------------------------------------// 616 617 printf("比特率 %3d\n", pFormatCtx->bit_rate); 618 printf("解码器名称 %s\n", paCodecCtx->codec->long_name); 619 printf("time_base %d \n", paCodecCtx->time_base); 620 printf("声道数 %d \n", paCodecCtx->channels); 621 printf("sample per second %d \n", paCodecCtx->sample_rate); 622 //--------------------------------------------------------// 623 624 //allocate video frame and set its fileds to default value 625 AVFrame *pFrame; 626 //AVFrame *pFrameYUV; 627 pFrame = av_frame_alloc(); 628 g_pFrameYUV = av_frame_alloc(); 629 630 //即使我们申请了一帧的内存,当转换的时候,我们仍然需要一个地方来放置原始 631 //的数据。我们使用avpicture_get_size 来获得我们需要的大小, 然后手工申请 632 //内存空间: 633 uint8_t *out_buffer; 634 int numBytes; 635 numBytes = avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); 636 //av_malloc 是ffmpeg 的malloc,用来实现一个简单的malloc 的包装,这样来保 637 //证内存地址是对齐的(4 字节对齐或者2 字节对齐)。它并不能保 护你不被内 638 //存泄漏,重复释放或者其它malloc 的问题所困扰。 639 out_buffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t)); 640 //Assign appropriate parts of buffer to image planes in pFrameYUV 641 //Note that pFrameYUV is an AVFrame, but AVFrame is a superset of AVPicture 642 avpicture_fill((AVPicture*)g_pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); 643 644 //----------------SDL--------------------------------------// 645 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){ 646 printf("Could not initialize SDL -%s\n", SDL_GetError()); 647 exit(1); 648 } 649 //先设置声音的选项:采样率,声音通道数和其它的参 数,然后我们 650 //设置一个回调函数和一些用户数据userdata。当开始播放音频的时候,SDL 将不 651 //断地调用这个回调函数并且要求它来向声音缓冲填入一个特定的数量的字节。 652 //当我们把这些信息放到SDL_AudioSpec 结构体中后,我们调用函数 653 //SDL_OpenAudio()就会打开声音设备并且给我们送 回另外一个AudioSpec 结构 654 //体。这个结构体是我们实际上用到的--因为我们不能保证得到我们所要求的。 655 SDL_AudioSpec wanted_spec; 656 wanted_spec.freq = paCodecCtx->sample_rate; 657 wanted_spec.format = AUDIO_S16SYS; 658 wanted_spec.channels = paCodecCtx->channels; //声音的通道数 659 wanted_spec.silence = 0; //用来表示静音的值 660 wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; //声音缓冲区的大小 661 wanted_spec.callback = audio_callback; 662 wanted_spec.userdata = paCodecCtx; 663 664 if (SDL_OpenAudio(&wanted_spec, NULL) < 0){ 665 printf("SDL_OpenAudio error: %s\n", SDL_GetError()); 666 return -1; 667 } 668 669 packet_queue_init(&audioq); 670 packet_queue_init(&videoq); 671 SDL_PauseAudio(0); 672 673 SDL_Window *window = nullptr; 674 window = SDL_CreateWindow("MPlayer", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 675 pCodecCtx->width, pCodecCtx->height, SDL_WINDOW_SHOWN); 676 if (!window){ 677 cout << SDL_GetError() << endl; 678 return 1; 679 } 680 681 SDL_Renderer *ren = nullptr; 682 ren = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); 683 if (ren == nullptr){ 684 cout << SDL_GetError() << endl; 685 return -1; 686 } 687 688 SDL_Texture *texture = nullptr; 689 texture = SDL_CreateTexture(ren, SDL_PIXELFORMAT_YV12, 690 SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height); 691 692 //*************************************************************// 693 //通过读取包来读取整个视频流,然后把它解码成帧,最后转换格式并且保存 694 int frameFinished; 695 //int psize = pCodecCtx->width * pCodecCtx->height; 696 AVPacket packet; 697 av_new_packet(&packet, numBytes); 698 699 i = 0; 700 int ret; 701 static struct SwsContext *img_convert_ctx; 702 img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, 703 pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, 704 SWS_BICUBIC, NULL, NULL, NULL); 705 706 SDL_Event ev; 707 708 video_thread_params vtp; 709 vtp.renderer = ren; 710 vtp.texture = texture; 711 vtp.sws_context = img_convert_ctx; 712 vtp.vid_codec_context = pCodecCtx; 713 vtp.video_mutex = SDL_CreateMutex(); 714 g_pVideoMutex = vtp.video_mutex; 715 g_pVideoThread = SDL_CreateThread(video_thread_proc, "video_thread", &vtp); 716 717 double v_a_ratio; // 视频帧数/音频帧数 718 int frame_queue_size; 719 720 //Read the next frame of a stream 721 while ((!quit) && (av_read_frame(pFormatCtx, &packet) >= 0 || (!frameq.empty()))) 722 { 723 //Is this a packet from the video stream? 724 if (packet.stream_index == videoIndex) 725 { 726 //decode video frame of size packet.size from packet.data into picture 727 ret = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); 728 //Did we get a video frame? 729 if (ret >= 0) 730 { 731 //Convert the image from its native format to YUV 732 if (frameFinished) 733 { 734 /* 735 sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, 736 pFrame->linesize, 0, pCodecCtx->height, g_pFrameYUV->data, g_pFrameYUV->linesize); 737 738 SDL_UpdateYUVTexture(texture, NULL, g_pFrameYUV->data[0], g_pFrameYUV->linesize[0], 739 g_pFrameYUV->data[1], g_pFrameYUV->linesize[1], g_pFrameYUV->data[2], g_pFrameYUV->linesize[2]); 740 741 AVFrame *pNewFrame = av_frame_clone(pFrame); 742 frameq.push(pNewFrame); 743 packet_queue_put(&videoq, &packet); 744 745 SDL_RenderClear(ren); 746 SDL_RenderCopy(ren, texture, NULL, NULL); 747 SDL_RenderPresent(ren); 748 // 可以使用 av_frame_clone() + 队列 实现远程 dts 读取 749 if (pCodecCtx->refcounted_frames) 750 { 751 av_frame_unref(pFrame); 752 } 753 cout << "<<<<<<" << endl; 754 cout << "vpts: " << packet.pts << ", apts: " << audio_pts << endl; 755 cout << "vdts: " << packet.dts << ", adts: " << audio_dts << endl; 756 */ 757 // 用互斥体保持同步,将包送入队列,让另外的线程自己处理 758 SDL_LockMutex(vtp.video_mutex); 759 packet_queue_put(&videoq, &packet); 760 AVFrame *pNewFrame = av_frame_clone(pFrame); 761 frameq.push(pNewFrame); 762 //cout << "Pushing vpacket." << endl; 763 SDL_UnlockMutex(vtp.video_mutex); 764 } 765 // 注意这里也必须要 free packet,否则会导致严重内存泄露 766 // 我修改了 packet_queue_put() 函数,它会复制 packet,所以可以放心释放上 767 av_free_packet(&packet); 768 } 769 else{ 770 av_free_packet(&packet); 771 cout << "decode error" << endl; 772 return -1; 773 } 774 } 775 else if (packet.stream_index == audioIndex){ 776 //packet_queue_put(&audioq, &packet); 777 /*ret = avcodec_decode_audio4(paCodecCtx, pFrame, &frameFinished, &packet); 778 cout << pFrame->format << endl; 779 780 if (ret < 0){ 781 printf("Error in decoding audio frame\n"); 782 exit(0); 783 } 784 if (frameFinished){ 785 printf("pts %5d\n", packet.pts); 786 printf("dts %5d\n", packet.dts); 787 printf("packet_size %5d\n", packet.size); 788 } 789 audio_chunk = (Uint8*)pFrame->data[0]; 790 audio_len = pFrame->linesize[0]; 791 792 audio_pos = audio_chunk; 793 //SDL_PauseAudio(0); 794 while (audio_len>0) 795 SDL_Delay(1);*/ 796 packet_queue_put(&audioq, &packet); 797 } 798 else 799 { 800 av_free_packet(&packet); 801 } 802 803 process_sdl_events: 804 if (SDL_PollEvent(&ev)) 805 { 806 switch (ev.type){ 807 case SDL_QUIT: 808 { 809 quit = 1; 810 video_quit = 1; 811 SDL_Quit(); 812 goto exit_line; 813 break; 814 } 815 case SDL_KEYDOWN: 816 if (ev.key.keysym.scancode == SDL_SCANCODE_ESCAPE) 817 { 818 quit = 1; 819 video_quit = 1; 820 SDL_Quit(); 821 goto exit_line; 822 break; 823 } 824 default: 825 break; 826 } 827 } 828 829 //cout << "vframes: " << pCodecCtx->frame_number << " aframes: " << paCodecCtx->frame_number << endl; 830 831 // 设置一个缓冲区大小,如果超过此大小则暂停处理(视频和音频)帧 832 // 这里采用限制 frameq 的大小的方法 833 // 如果采用以下的动态代码,有可能导致结尾“崩坏”,囧 834 /* 835 if (paCodecCtx->frame_number == 0) 836 { 837 v_a_ratio = 300; // 一个很大的值,基本上能保证所有视频都能解码 838 } 839 else 840 { 841 v_a_ratio = pCodecCtx->frame_number / (double)paCodecCtx->frame_number; 842 if (v_a_ratio < 10.0) v_a_ratio = 10.0; 843 } 844 845 frame_queue_size = (int)v_a_ratio * 2; 846 if (frameq.size() > frame_queue_size) goto process_sdl_events; 847 */ 848 if (frameq.size() > 50) goto process_sdl_events; 849 } 850 851 exit_line: 852 853 SDL_DestroyMutex(vtp.video_mutex); 854 855 SDL_DestroyTexture(texture); 856 857 av_frame_free(&pFrame); 858 av_frame_free(&g_pFrameYUV); 859 860 avcodec_close(pCodecCtx); 861 862 avformat_close_input(&pFormatCtx); 863 864 return 0; 865 }