在上一篇中简单设计了一个视频播放器,但是没有声音,于是本篇的代码就是在上一篇的基础上加上声音。
在ffmpeg给的官方例程中,给视频添加声音是有问题的,这一块可以参考下面的这篇博客:
http://www.cnblogs.com/ansersion/p/5265033.html
其中播放声音比较ffmpeg以前版本增加了重采样的概念,而我添加声音这块代码也是参考这篇博客得来。
代码是在上一篇的基础上添加,对于之前的代码几乎没有删改,程序最终运行效果为视频跑的很快,但是声音则正常播放。
开发环境:
操作系统:ubuntu14
ffmpeg版本:3.2.2
sdl版本:2
编译与运行:
gcc -g main.c -o test -I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -lavutil -lavformat -lavcodec -lz -lavutil -lswscale -L /usr/lib/x86_64-linux-gnu -lSDL2 -lSDL2main
(其中路径按照自己的安装路径来 -g 是加GDB调试)
./test
1 #ifdef _cplusplus 2 extern "C" 3 { 4 #endif 5 6 #include<stdio.h> 7 #include<assert.h> 8 #include<libavcodec/avcodec.h> 9 #include<libavformat/avformat.h> 10 #include<libavutil/avutil.h> 11 #include<libswscale/swscale.h> 12 #include <libswresample/swresample.h> 13 #include<libavutil/avutil.h> 14 #include<libavutil/imgutils.h> 15 #include<SDL2/SDL.h> 16 #include<SDL2/SDL_thread.h> 17 18 //是否将YUV420P内容输出到文件 19 #define OUTPUT_YUV420P 0 20 //要播放的文件路径 21 #define filename "/home/sns/test.flv" 22 //要输出YUV420P内容的文件路径 23 #define outfilename "/home/sns/output.yuv" 24 25 #define SDL_AUDIO_BUFFER_SIZE 1024 26 #define MAX_AUDIO_FRAME_SIZE 192000 27 //结构体定义 28 typedef struct PacketQueue{ 29 AVPacketList *first_pkt,*last_pkt;//队首、队尾 30 int nb_packets; //包的个数 31 int size;//队列的字节数 32 SDL_mutex *mutex;//互斥量 33 SDL_cond *cond;//条件变量 34 }PacketQueue; 35 36 typedef struct AudioParams { 37 int freq; 38 int channels; 39 int64_t channel_layout; 40 enum AVSampleFormat fmt; 41 int frame_size; 42 int bytes_per_sec; 43 } AudioParams; 44 //函数定义 45 static void audio_callback(void *userdata, Uint8 * stream, int len);// 46 static int packet_queue_get(PacketQueue *q,AVPacket *pkt,int block); 47 int packet_queue_put(PacketQueue *q,AVPacket *pkt); 48 void packet_queue_init(PacketQueue *q); 49 static int audio_decode_frame(AVCodecContext *aCodecCtx,uint8_t *audio_buf,int buf_size); 50 int resample(AVFrame *af,uint8_t *audio_buf,int *audio_buf_size); 51 52 //全局变量定义 53 int quit =0; 54 PacketQueue audioQ; 55 int sample_rate, nb_channels; 56 int64_t channel_layout; 57 struct SwrContext * swr_ctx = NULL; 58 AudioParams audio_hw_params_tgt; 59 AudioParams audio_hw_params_src; 60 61 //主函数 62 int main(int argc, char **argv) 63 { 64 //变量定义********************************************************************* 65 AVFormatContext *pFormatCtx; 66 int i=0; 67 int videoStream; 68 int audioStream; 69 AVCodecContext *pCodecCtx; 70 AVCodecContext *aCodecCtxOrig; 71 AVCodecContext *aCodecCtx; 72 AVCodec *pCodec; 73 AVCodec *aCodec; 74 AVFrame *pFrame; 75 AVFrame *pFrameYUV; 76 uint8_t *buffer; 77 int numBytes; 78 79 SDL_Window *screen; 80 SDL_Renderer *sdlRender; 81 SDL_Texture *sdlTexture; 82 SDL_Rect sdlRect; 83 int frameFinished; 84 AVPacket packet; 85 struct SwsContext *img_convert_ctx; 86 int err_code; 87 char buf[1024]; 88 FILE *fp_yuv; 89 int y_size; 90 SDL_AudioSpec audioSpec; 91 SDL_AudioSpec spec; 92 SDL_Event event; 93 //******************************************************************************* 94 av_register_all(); 95 //1、打开视频文件************************************************* 96 pFormatCtx = avformat_alloc_context(); 97 err_code = avformat_open_input(&pFormatCtx, filename, NULL, NULL); 98 if (err_code != 0) 99 {//打开文件失败 100 av_strerror(err_code, buf, 1024); 101 printf("coundn't open the file!,error code = %d(%s)\n", err_code, buf); 102 return -1; 103 } 104 if (avformat_find_stream_info(pFormatCtx, NULL) < 0) 105 { 106 printf("Couldn't find stream information.\n"); 107 return -1; 108 } 109 // 打印信息 110 av_dump_format(pFormatCtx, 0, filename, 0); 111 //2、找到第一个视频流和第一个音频流**************************** 112 videoStream = -1; 113 audioStream = -1; 114 for (i = 0; i < pFormatCtx->nb_streams; i++) 115 { 116 if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && videoStream<0) 117 { 118 videoStream = i;//得到视频流的索引 119 } 120 if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO &&audioStream < 0) 121 { 122 audioStream = i;//得到音频流的索引 123 } 124 } 125 if (videoStream == -1) 126 { 127 printf("Didn't find a video stream.\n"); 128 return -1; 129 } 130 if(audioStream == -1) 131 { 132 printf("coundn't find a audio stream!\n"); 133 return -1; 134 } 135 /* 3、从视频流中得到一个音频和视频编解码上下文,里面包含了编解码器的所有信息和一个 136 指向真正的编解码器 ,然后我们找到音频和视频编解码器*/ 137 pCodecCtx = pFormatCtx->streams[videoStream]->codec; 138 aCodecCtxOrig = pFormatCtx->streams[audioStream]->codec; 139 pCodec = avcodec_find_decoder(pCodecCtx->codec_id); 140 aCodec = avcodec_find_decoder(aCodecCtxOrig->codec_id); 141 if (pCodec == NULL) 142 { 143 fprintf(stderr, "Unsupported codec !\n"); 144 return -1; 145 } 146 if(aCodec == NULL) 147 { 148 fprintf(stderr,"Unsupported codec!\n"); 149 return -1; 150 } 151 //拷贝上下文 152 aCodecCtx = avcodec_alloc_context3(aCodec); 153 if(avcodec_copy_context(aCodecCtx,aCodecCtxOrig) != 0) 154 { 155 fprintf(stderr,"couldn't copy codec context!\n"); 156 return -1; 157 } 158 //4、打开音频和视频编解码器 159 if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) 160 { 161 printf("cann't open the codec!\n"); 162 return -1; 163 } 164 if(avcodec_open2(aCodecCtx,aCodec,NULL) < 0 ) 165 { 166 printf("cann't open the audio codec!\n"); 167 return -1; 168 } 169 //设置声音参数 170 sample_rate = aCodecCtx->sample_rate; 171 nb_channels = aCodecCtx->channels; 172 channel_layout = aCodecCtx->channel_layout; 173 174 // printf("channel_layout=%" PRId64 "\n", channel_layout); 175 printf("nb_channels=%d\n", nb_channels); 176 printf("freq=%d\n", sample_rate); 177 178 if (!channel_layout|| nb_channels != av_get_channel_layout_nb_channels(channel_layout)) 179 { 180 channel_layout = av_get_default_channel_layout(nb_channels); 181 channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX; 182 printf("correction\n"); 183 } 184 /*通故编解码上下文中的所有信息来建立音频的信息*/ 185 audioSpec.freq = aCodecCtx->sample_rate; 186 audioSpec.format = AUDIO_S16SYS; 187 audioSpec.channels = aCodecCtx->channels; 188 audioSpec.silence = 0; 189 audioSpec.samples = SDL_AUDIO_BUFFER_SIZE; 190 audioSpec.callback = audio_callback; 191 audioSpec.userdata = aCodecCtx; 192 //打开音频设备和初始化 193 if(SDL_OpenAudio(&audioSpec,&spec) < 0) 194 //其中回调函数在需要更多音频数据的时候被调用(即播放完后需要从回调取数据播放) 195 { 196 fprintf(stderr,"SDL_OpenAudio: %s\n",SDL_GetError()); 197 return -1; 198 } 199 printf("freq: %d\tchannels: %d\n", spec.freq, spec.channels); 200 //5、分配两个视频帧,一个保存得到的原始视频帧,一个保存为指定格式的视频帧(该帧通过原始帧转换得来) 201 pFrame = av_frame_alloc(); 202 if (pFrame == NULL) 203 { 204 printf("pFrame alloc fail!\n"); 205 return -1; 206 } 207 pFrameYUV = av_frame_alloc(); 208 if (pFrameYUV == NULL) 209 { 210 printf("pFrameYUV alloc fail!\n"); 211 return -1; 212 } 213 //6、得到一帧视频截图的内存大小并分配内存,并将YUV数据填充进去 214 numBytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, 215 pCodecCtx->height,1); 216 buffer = (uint8_t*) av_mallocz(numBytes * sizeof(uint8_t)); 217 if (!buffer) 218 { 219 printf("numBytes :%d , buffer malloc 's mem \n", numBytes); 220 return -1; 221 } 222 //打印信息 223 printf("--------------- File Information ----------------\n"); 224 av_dump_format(pFormatCtx, 0, filename, 0); 225 printf("-------------------------------------------------\n"); 226 av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,buffer, 227 AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1); 228 //7、得到指定转换格式的上下文********************************** 229 img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, 230 pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 231 AV_PIX_FMT_YUV420P, 232 SWS_BICUBIC, 233 NULL, NULL, NULL); 234 if (img_convert_ctx == NULL) 235 { 236 fprintf(stderr, "Cannot initialize the conversion context!\n"); 237 return -1; 238 } 239 //*********************************************************** 240 #if OUTPUT_YUV420P 241 fp_yuv = fopen(outfilename, "wb+"); 242 #endif 243 //8、SDL初始化和创建多重windows等准备工作 244 if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_VIDEO)) 245 { 246 fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError()); 247 return -1; 248 } 249 //使用SDL_CreateWindow代替SDL_SetVideoMode 250 //创建一个给定高度和宽度、位置和标示的windows。 251 screen = SDL_CreateWindow("Simplest ffmpeg player's Window", 252 SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, pCodecCtx->width, 253 pCodecCtx->height, SDL_WINDOW_OPENGL); 254 if (!screen) 255 { 256 fprintf(stderr, "SDL: could not create window - exiting - %s\n",SDL_GetError()); 257 return -1; 258 } 259 //对该window创建一个2D渲染上下文 260 sdlRender = SDL_CreateRenderer(screen, -1, 0); 261 if (!sdlRender) 262 { 263 fprintf(stderr, "SDL:cound not create render : %s\n", SDL_GetError()); 264 return -1; 265 } 266 //Create a texture for a rendering context. 267 //为一个渲染上下文创建一个纹理 268 //IYUV: Y + U + V (3 planes) 269 //YV12: Y + V + U (3 planes) 270 sdlTexture = SDL_CreateTexture(sdlRender, SDL_PIXELFORMAT_IYUV, 271 SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height); 272 if (!sdlTexture) 273 { 274 fprintf(stderr, "SDL:cound not create Texture : %s\n", SDL_GetError()); 275 return -1; 276 } 277 //建立一个矩形变量,提供后面使用 278 sdlRect.x = 0; 279 sdlRect.y = 0; 280 sdlRect.w = pCodecCtx->width; 281 sdlRect.h = pCodecCtx->height; 282 //***************************************************** 283 //声音部分代码 284 audio_hw_params_tgt.fmt = AV_SAMPLE_FMT_S16; 285 audio_hw_params_tgt.freq = spec.freq; 286 audio_hw_params_tgt.channel_layout = channel_layout; 287 audio_hw_params_tgt.channels = spec.channels; 288 audio_hw_params_tgt.frame_size = av_samples_get_buffer_size(NULL, 289 audio_hw_params_tgt.channels, 1, audio_hw_params_tgt.fmt, 1); 290 audio_hw_params_tgt.bytes_per_sec = av_samples_get_buffer_size(NULL, 291 audio_hw_params_tgt.channels, audio_hw_params_tgt.freq, 292 audio_hw_params_tgt.fmt, 1); 293 if (audio_hw_params_tgt.bytes_per_sec <= 0|| audio_hw_params_tgt.frame_size <= 0) 294 { 295 printf("size error\n"); 296 return -1; 297 } 298 audio_hw_params_src = audio_hw_params_tgt; 299 //***************************************************** 300 packet_queue_init(&audioQ); 301 SDL_PauseAudio(0); 302 //9、正式开始读取数据***************************************** 303 while (av_read_frame(pFormatCtx, &packet) >= 0) 304 { 305 //如果读取的包来自视频流 306 if (packet.stream_index == videoStream) 307 { 308 //从包中得到解码后的帧 309 if (avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,&packet) < 0) 310 { 311 printf("Decode Error!\n"); 312 return -1; 313 } 314 //如果确定完成得到该视频帧 315 if (frameFinished) 316 { 317 //转换帧数据格式 318 sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, 319 pCodecCtx->height, 320 pFrameYUV->data, 321 pFrameYUV->linesize); 322 #if OUTPUT_YUV420P 323 y_size = pCodecCtx->width * pCodecCtx->height; 324 fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv); //Y 325 fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv); //U 326 fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv); //V 327 #endif 328 //SDL显示~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 329 #if 0 330 SDL_UpdateTexture(sdlTexture,NULL,pFrameYUV->data[0],pFrameYUV->linesize[0]); 331 #else 332 SDL_UpdateYUVTexture(sdlTexture, &sdlRect, pFrameYUV->data[0], 333 pFrameYUV->linesize[0], pFrameYUV->data[1], 334 pFrameYUV->linesize[1], pFrameYUV->data[2], 335 pFrameYUV->linesize[2]); 336 #endif 337 SDL_RenderClear(sdlRender); 338 SDL_RenderCopy(sdlRender, sdlTexture, NULL, &sdlRect); 339 SDL_RenderPresent(sdlRender); 340 //结束SDL~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 341 } 342 } 343 else if(packet.stream_index == audioStream) 344 { 345 packet_queue_put(&audioQ,&packet); 346 }else{ 347 av_free_packet(&packet);//释放读出来的包 348 } 349 SDL_PollEvent(&event); 350 switch (event.type) 351 { 352 case SDL_QUIT: 353 quit = 1; 354 SDL_Quit(); 355 exit(0); 356 break; 357 default: 358 break; 359 } 360 } 361 while(1) SDL_Delay(1000); 362 //************************************************************************************** 363 //10、释放分配的内存或关闭文件等操作 364 #if OUTPUT_YUV420P 365 fclose(fp_yuv); 366 #endif 367 sws_freeContext(img_convert_ctx); 368 SDL_Quit(); 369 av_free(buffer); 370 av_free(pFrame); 371 av_free(pFrameYUV); 372 avcodec_close(pCodecCtx); 373 avcodec_close(aCodecCtxOrig); 374 avcodec_close(aCodecCtx); 375 avformat_close_input(&pFormatCtx); 376 return EXIT_SUCCESS; 377 } 378 379 380 static void audio_callback(void *userdata, Uint8 * stream, int len) 381 { 382 AVCodecContext *aCodecCtx = (AVCodecContext*)userdata; 383 int len1,audio_size; 384 static uint8_t audio_buf[(MAX_AUDIO_FRAME_SIZE*3)/2]; 385 static unsigned int audio_buf_size = 0; 386 static unsigned int audio_buf_index =0; 387 while(len>0){ 388 if(audio_buf_index >= audio_buf_size){ 389 audio_size = audio_decode_frame(aCodecCtx,audio_buf,sizeof(audio_buf)); 390 if(audio_size < 0){ 391 audio_buf_size = 1024; 392 memset(audio_buf,0,audio_buf_size); 393 }else{ 394 audio_buf_size = audio_size; 395 } 396 audio_buf_index = 0; 397 } 398 len1 = audio_buf_size - audio_buf_index; 399 if(len1 > len) 400 len1 = len; 401 memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1); 402 len -= len1; 403 stream += len1; 404 audio_buf_index += len1; 405 } 406 } 407 408 void packet_queue_init(PacketQueue *q) 409 { 410 memset(q,0,sizeof(PacketQueue)); 411 q->mutex = SDL_CreateMutex(); 412 q->cond = SDL_CreateCond(); 413 } 414 415 int packet_queue_put(PacketQueue *q,AVPacket *pkt) 416 { 417 AVPacketList *pkt1; 418 if(av_dup_packet(pkt)<0) 419 { 420 printf("dup packet error!\n"); 421 return -1; 422 } 423 pkt1 = av_malloc(sizeof(AVPacketList)); 424 if(!pkt1) 425 { 426 printf("malloc AVPacketList error!\n"); 427 return -1; 428 } 429 pkt1->pkt = *pkt; 430 pkt1->next = NULL; 431 SDL_LockMutex(q->mutex); 432 if(!q->last_pkt){ 433 q->first_pkt = pkt1; 434 }else{ 435 q->last_pkt->next = pkt1; 436 } 437 q->last_pkt = pkt1; 438 q->nb_packets ++; 439 q->size +=pkt1->pkt.size; 440 SDL_CondSignal(q->cond); 441 SDL_UnlockMutex(q->mutex); 442 return 0; 443 } 444 static int packet_queue_get(PacketQueue *q,AVPacket *pkt,int block){ 445 int ret; 446 AVPacketList *pkt1; 447 SDL_LockMutex(q->mutex); 448 for(;;){ 449 if(quit){ 450 printf("packet_queue has quit!\n"); 451 ret =-1; 452 break; 453 } 454 pkt1 = q->first_pkt; 455 if(pkt1){ 456 q->first_pkt = pkt1->next; 457 if(!q->first_pkt){ 458 q->last_pkt = NULL; 459 } 460 q->nb_packets--; 461 q->size -= pkt1->pkt.size; 462 *pkt = pkt1->pkt; 463 av_free(pkt1); 464 ret =1; 465 break; 466 }else if(!block){ 467 ret =0; 468 break; 469 }else{ 470 SDL_CondWait(q->cond,q->mutex);//做了解锁互斥量的动作 471 } 472 } 473 SDL_UnlockMutex(q->mutex); 474 return ret; 475 } 476 static int audio_decode_frame(AVCodecContext *aCodecCtx,uint8_t *audio_buf,int buf_size) 477 { 478 static AVPacket pkt; 479 static uint8_t *audio_pkt_data = NULL; 480 static int audio_pkt_size = 0; 481 int len1,data_size=0; 482 static AVFrame frame; 483 int got_frame=0; 484 485 for(;;){ 486 while(audio_pkt_size >0 ){ 487 len1 = avcodec_decode_audio4(aCodecCtx, &frame, &got_frame, &pkt); 488 if (len1 < 0) 489 { 490 /* if error, skip frame */ 491 audio_pkt_size = 0; 492 break; 493 } 494 audio_pkt_data += len1; 495 audio_pkt_size -= len1; 496 data_size =0; 497 if(got_frame){ 498 data_size = resample(&frame, audio_buf, &buf_size); 499 assert(data_size <= buf_size); 500 } 501 if (data_size <= 0) 502 { 503 /* No data yet, get more frames */ 504 continue; 505 } 506 /* We have data, return it and come back for more later */ 507 return data_size; 508 } 509 if (pkt.data) 510 av_free_packet(&pkt); 511 if (quit) 512 { 513 return -1; 514 } 515 if (packet_queue_get(&audioQ, &pkt, 1) < 0) 516 { 517 printf("packet_queue_get error!\n"); 518 return -1; 519 } 520 audio_pkt_data = pkt.data; 521 audio_pkt_size = pkt.size; 522 } 523 return 1; 524 } 525 526 //重采样 527 int resample(AVFrame *af, uint8_t *audio_buf, int *audio_buf_size) 528 { 529 int data_size = 0; 530 int resampled_data_size = 0; 531 int64_t dec_channel_layout; 532 data_size = av_samples_get_buffer_size(NULL, 533 av_frame_get_channels(af), 534 af->nb_samples, 535 af->format, 1); 536 dec_channel_layout =(af->channel_layout&& 537 av_frame_get_channels(af)== av_get_channel_layout_nb_channels( 538 af->channel_layout)) ? 539 af->channel_layout : 540 av_get_default_channel_layout(av_frame_get_channels(af)); 541 if (af->format != audio_hw_params_src.fmt 542 || af->sample_rate != audio_hw_params_src.freq 543 || dec_channel_layout != audio_hw_params_src.channel_layout 544 || !swr_ctx) 545 { 546 swr_free(&swr_ctx); 547 swr_ctx = swr_alloc_set_opts(NULL, audio_hw_params_tgt.channel_layout, 548 audio_hw_params_tgt.fmt, audio_hw_params_tgt.freq, 549 dec_channel_layout, af->format, af->sample_rate, 0, NULL); 550 if (!swr_ctx || swr_init(swr_ctx) < 0) 551 { 552 av_log(NULL, AV_LOG_ERROR, 553 "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n", 554 af->sample_rate, av_get_sample_fmt_name(af->format), 555 av_frame_get_channels(af), audio_hw_params_tgt.freq, 556 av_get_sample_fmt_name(audio_hw_params_tgt.fmt), 557 audio_hw_params_tgt.channels); 558 swr_free(&swr_ctx); 559 return -1; 560 } 561 printf("swr_init\n"); 562 audio_hw_params_src.channels = av_frame_get_channels(af); 563 audio_hw_params_src.fmt = af->format; 564 audio_hw_params_src.freq = af->sample_rate; 565 } 566 567 if (swr_ctx) 568 { 569 const uint8_t **in = (const uint8_t **) af->extended_data; 570 uint8_t **out = &audio_buf; 571 int out_count = (int64_t) af->nb_samples * audio_hw_params_tgt.freq 572 / af->sample_rate + 256; 573 int out_size = av_samples_get_buffer_size(NULL, 574 audio_hw_params_tgt.channels, out_count, 575 audio_hw_params_tgt.fmt, 0); 576 int len2; 577 if (out_size < 0) 578 { 579 av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size() failed\n"); 580 return -1; 581 } 582 av_fast_malloc(&audio_buf, audio_buf_size, out_size); 583 if (!audio_buf) 584 return AVERROR(ENOMEM); 585 len2 = swr_convert(swr_ctx, out, out_count, in, af->nb_samples); 586 if (len2 < 0) 587 { 588 av_log(NULL, AV_LOG_ERROR, "swr_convert() failed\n"); 589 return -1; 590 } 591 if (len2 == out_count) 592 { 593 av_log(NULL, AV_LOG_WARNING, 594 "audio buffer is probably too small\n"); 595 if (swr_init(swr_ctx) < 0) 596 swr_free(&swr_ctx); 597 } 598 resampled_data_size = len2 * audio_hw_params_tgt.channels 599 * av_get_bytes_per_sample(audio_hw_params_tgt.fmt); 600 } 601 else 602 { 603 audio_buf = af->data[0]; 604 resampled_data_size = data_size; 605 } 606 607 return resampled_data_size; 608 } 609 610 #ifdef _cplusplus 611 } 612 #endif
代码那么多,不好理解呀~~~~~
所以程序还是最好模块化和分文件来做才行,或者封装成一个类。