注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

_

_

 
 
 

日志

 
 

使用ffmpeg+SDL的简单播放器  

2013-03-14 18:31:51|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

?

?

?

  1. #include "libavcodec/avcodec.h"
  2. #include "libavformat/avformat.h"
  3. #include "libswscale/swscale.h"
  4. #include <stdio.h>
  5. #include "SDL/SDL.h"
  6. #include "SDL_thread.h"
  7. #define PICTURE_W 720
  8. #define PICTURE_H 576
  9. #define PICTURE_PW 720
  10. #define PICTURE_PH 576
  11. #define MAX_QUEUE_SIZE 100
  12. #define VIDEO_PICTURE_QUEUE_SIZE 2
  13. typedef struct VideoPicture_t{
  14. SDL_Overlay *bmp;
  15. double pts;
  16. double duration;
  17. int width,height;
  18. }VideoPicture;
  19. struct play_info_t{
  20. double audio_clock;//解码时钟
  21. double audio_current_pts;//当前音频的pts
  22. double vedio_clock;//解码时钟
  23. double last_frame_pts;//上一帧的PTS值
  24. double last_frame_delay;//上一帧的延时
  25. double frame_timer;
  26. AVCodecContext *pVideoCodecCtx;
  27. AVCodecContext *pAudioCodecCtx;
  28. VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE];
  29. int pictq_size, pictq_rindex, pictq_windex;
  30. SDL_mutex *pictq_mutex;
  31. SDL_cond *pictq_cond;
  32. struct SwrContext *audio_conv;
  33. struct SwsContext *video_conv;
  34. };
  35. struct play_info_t g_play_info;
  36. typedef struct PacketQueue {
  37. AVPacketList *first_pkt, *last_pkt;
  38. int nb_packets;
  39. int size;
  40. SDL_mutex *mutex;
  41. SDL_cond *cond;
  42. } PacketQueue;
  43. PacketQueue audio_queue;
  44. PacketQueue video_queue;
  45. int quit = 0;
  46. SDL_Surface *screen;
  47. SDL_Overlay *bmp;
  48. SDL_Rect rect;
  49. int global_video_pkt_pts=0;
  50. int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) {
  51. int ret = avcodec_default_get_buffer(c, pic);
  52. uint64_t *pts = av_malloc(sizeof(uint64_t));
  53. *pts = global_video_pkt_pts;
  54. pic->opaque = pts;
  55. return ret;
  56. }
  57. void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) {
  58. if(pic) av_freep(&pic->opaque);
  59. avcodec_default_release_buffer(c, pic);
  60. }
  61. double get_audio_clock(void)
  62. {
  63. return g_play_info.audio_current_pts;
  64. }
  65. double get_video_clock(void)
  66. {
  67. return g_play_info.vedio_clock;
  68. }
  69. void picture_queque_init(struct play_info_t *play)
  70. {
  71. int i;
  72. play->pictq_cond=SDL_CreateCond();
  73. play->pictq_mutex=SDL_CreateMutex();
  74. play->pictq_rindex=play->pictq_windex=0;
  75. play->pictq_size=0;
  76. memset(play->pictq,0,sizeof(play->pictq));
  77. for(i=0;i<VIDEO_PICTURE_QUEUE_SIZE;i++){
  78. play->pictq[i].bmp=SDL_CreateYUVOverlay(PICTURE_W,PICTURE_H,SDL_YUY2_OVERLAY,screen);
  79. }
  80. }
  81. void picture_queque_destroy(struct play_info_t *play)
  82. {
  83. int i;
  84. for(i=0;i<VIDEO_PICTURE_QUEUE_SIZE;i++){
  85. SDL_FreeYUVOverlay(play->pictq[i].bmp);
  86. }
  87. }
  88. void packet_queue_init(PacketQueue *q) {
  89. memset(q, 0, sizeof(PacketQueue));
  90. q->mutex = SDL_CreateMutex();
  91. q->cond = SDL_CreateCond();
  92. }
  93. int packet_queue_put(PacketQueue *q, AVPacket *pkt) {
  94. AVPacketList *pkt1;
  95. if(av_dup_packet(pkt) < 0) {
  96. return -1;
  97. }
  98. pkt1 = av_malloc(sizeof(AVPacketList));
  99. if (!pkt1)
  100. return -1;
  101. pkt1->pkt = *pkt;
  102. pkt1->next = NULL;
  103. SDL_LockMutex(q->mutex);
  104. if (!q->last_pkt)
  105. q->first_pkt = pkt1;
  106. else
  107. q->last_pkt->next = pkt1;
  108. q->last_pkt = pkt1;
  109. q->nb_packets++;
  110. q->size += pkt1->pkt.size;
  111. SDL_CondSignal(q->cond);
  112. SDL_UnlockMutex(q->mutex);
  113. return 0;
  114. }
  115. static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {
  116. AVPacketList *pkt1;
  117. int ret;
  118. SDL_LockMutex(q->mutex);
  119. for(;;) {
  120. if(quit) {
  121. ret = -1;
  122. break;
  123. }
  124. pkt1 = q->first_pkt;
  125. if (pkt1) {
  126. q->first_pkt = pkt1->next;
  127. if (!q->first_pkt)
  128. q->last_pkt = NULL;
  129. q->nb_packets--;
  130. q->size -= pkt1->pkt.size;
  131. *pkt = pkt1->pkt;
  132. av_free(pkt1);
  133. ret = 1;
  134. break;
  135. } else if (!block) {
  136. ret = 0;
  137. break;
  138. } else {
  139. SDL_CondWait(q->cond, q->mutex);
  140. }
  141. }
  142. SDL_UnlockMutex(q->mutex);
  143. return ret;
  144. }
  145. int decode_interrupt_cb(void) {
  146. return quit;
  147. }
  148. //计算正确的pts值
  149. double sync_video(struct play_info_t *play,AVFrame* frame,double pts)
  150. {
  151. double frame_delay;
  152. if(pts!=0)
  153. play->vedio_clock=pts;
  154. else
  155. pts=play->vedio_clock;
  156. frame_delay=av_q2d(play->pVideoCodecCtx->time_base);//一帧占用的时间
  157. frame_delay+=frame->repeat_pict*(frame_delay*0.5);//计算重复帧
  158. play->vedio_clock+=frame_delay;
  159. return pts;
  160. }
  161. int audio_decode_frame(struct play_info_t* play, uint8_t *audio_buf,int buf_size) {
  162. AVCodecContext *aCodecCtx=play->pAudioCodecCtx;
  163. AVFrame *pAudioFrame=avcodec_alloc_frame();
  164. AVPacket pkt,pkt1;
  165. int frame_finished=0;
  166. int pkt_pos,pkt_len;
  167. int src_len=0,dst_len=0,data_size=0;
  168. float pts=0;
  169. avcodec_get_frame_defaults(pAudioFrame);
  170. uint8_t *out[]={audio_buf};
  171. for(;!quit;){
  172. if(packet_queue_get(&audio_queue, &pkt, 1) < 0) {
  173. av_free(pAudioFrame);
  174. return -1;
  175. }
  176. pkt1=pkt;
  177. pkt_pos=0;
  178. pkt_len=pkt.size;
  179. while(pkt_pos<pkt.size && !quit){
  180. if((src_len=avcodec_decode_audio4(aCodecCtx,pAudioFrame,&frame_finished,&pkt1))<0){
  181. av_free_packet(&pkt);
  182. av_free(pAudioFrame);
  183. return -1;
  184. }
  185. play->audio_clock+=(double)(pAudioFrame->linesize[0])/
  186. (aCodecCtx->channels*aCodecCtx->sample_rate*av_get_bytes_per_sample(aCodecCtx->sample_fmt));
  187. if(frame_finished){
  188. int len=swr_convert(play->audio_conv,out,buf_size/aCodecCtx->channels/av_get_bytes_per_sample(AV_SAMPLE_FMT_S16),
  189. pAudioFrame->data,pAudioFrame->linesize[0]/aCodecCtx->channels/av_get_bytes_per_sample(pAudioFrame->format));
  190. len=len*aCodecCtx->channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
  191. av_free(pAudioFrame);
  192. av_free_packet(&pkt);
  193. return len;
  194. }else{
  195. if (!pkt1.data && aCodecCtx->codec->capabilities & CODEC_CAP_DELAY){
  196. break;
  197. }
  198. }
  199. pkt_pos+=src_len;//已经解码的长度
  200. pkt1.data=pkt.data+pkt_pos;
  201. pkt1.size=pkt.size-pkt_pos;
  202. }
  203. av_free_packet(&pkt);
  204. }
  205. av_free(pAudioFrame);
  206. return dst_len;
  207. }
  208. //decode the audio data ,and copy the result to stream
  209. void SDLCALL audio_callback(void *userdata, Uint8 *stream, int len)
  210. {
  211. struct play_info_t *play=(struct play_info_t*)userdata;
  212. AVCodecContext *aCodecCtx = play->pAudioCodecCtx;
  213. int len1, audio_size;
  214. static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
  215. static unsigned int audio_buf_size = 0;
  216. static unsigned int audio_buf_index = 0;
  217. int bytes_per_sec=2*aCodecCtx->channels*aCodecCtx->sample_rate;
  218. while(len > 0 && !quit) {
  219. if(audio_buf_index >= audio_buf_size) {
  220. audio_size = audio_decode_frame(play, audio_buf,sizeof(audio_buf));
  221. if(audio_size < 0) {
  222. audio_buf_size = 1024;
  223. memset(audio_buf, 0, audio_buf_size);
  224. } else {
  225. audio_buf_size = audio_size;
  226. }
  227. audio_buf_index = 0;
  228. }
  229. len1 = audio_buf_size - audio_buf_index;
  230. if(len1 > len)
  231. len1 = len;
  232. memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);
  233. len -= len1;
  234. stream += len1;
  235. audio_buf_index += len1;
  236. }
  237. if(audio_buf_size > audio_buf_index){
  238. play->audio_current_pts=play->audio_clock-((double)(audio_buf_size-audio_buf_index)/(double)bytes_per_sec);
  239. if(play->audio_current_pts<0)
  240. play->audio_current_pts=play->audio_clock;
  241. //printf("audio-pts:%f\n",play->audio_current_pts);
  242. }
  243. }
  244. int SDLCALL video_decode_callback(void* arg)
  245. {
  246. printf("---in video decode thread ---\n");
  247. struct play_info_t* play=(struct play_info_t*)arg;
  248. AVCodecContext *pVideoCodecCtx=play->pVideoCodecCtx;
  249. double pts;
  250. VideoPicture *vp;
  251. AVPacket pkt;
  252. int frame_finished;
  253. AVPicture pict = { { 0 } };
  254. AVFrame *pVideoFrame=avcodec_alloc_frame();
  255. if(pVideoFrame==NULL){
  256. printf("can't alloc video frame!\n");
  257. return -1;
  258. }
  259. while(!quit){
  260. if(packet_queue_get(&video_queue, &pkt, 1) < 0) {
  261. return -1;
  262. }
  263. pts = 0;
  264. // Save global pts to be stored in pFrame in first call
  265. global_video_pkt_pts = pkt.pts;
  266. avcodec_decode_video2(pVideoCodecCtx,pVideoFrame,&frame_finished,&pkt);
  267. if(pkt.dts == AV_NOPTS_VALUE && pVideoFrame->opaque && *(uint64_t*)pVideoFrame->opaque != AV_NOPTS_VALUE){
  268. pts = *(uint64_t *)pVideoFrame->opaque;
  269. } else if(pkt.dts!=AV_NOPTS_VALUE){
  270. pts=pkt.dts;
  271. }else{
  272. pts=0;
  273. }
  274. pts*=av_q2d(pVideoCodecCtx->time_base);
  275. av_free_packet(&pkt);
  276. if(frame_finished){
  277. SDL_LockMutex(play->pictq_mutex);
  278. while(play->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !quit){
  279. SDL_CondWait(play->pictq_cond,play->pictq_mutex);
  280. }
  281. SDL_UnlockMutex(play->pictq_mutex);
  282. if(quit)
  283. goto out;
  284. vp=&play->pictq[play->pictq_windex];
  285. SDL_Overlay *bmp=vp->bmp;
  286. //序列化帧
  287. pts=sync_video(&g_play_info,pVideoFrame,pts);
  288. vp->pts=pts;
  289. //printf("video-pts:%f\n",pts);
  290. SDL_LockYUVOverlay(bmp);
  291. pict.data[0]=bmp->pixels[0];
  292. pict.data[1]=bmp->pixels[2];
  293. pict.data[2]=bmp->pixels[1];
  294. pict.linesize[0]=bmp->pitches[0];
  295. pict.linesize[1]=bmp->pitches[2];
  296. pict.linesize[2]=bmp->pitches[1];
  297. sws_scale(play->video_conv,pVideoFrame->data,pVideoFrame->linesize,0,
  298. pVideoFrame->height,pict.data,pict.linesize);
  299. SDL_UnlockYUVOverlay(bmp);
  300. play->pictq_windex=(play->pictq_windex+1)%VIDEO_PICTURE_QUEUE_SIZE;
  301. SDL_LockMutex(play->pictq_mutex);
  302. play->pictq_size++;
  303. SDL_UnlockMutex(play->pictq_mutex);
  304. }
  305. }
  306. out:
  307. av_free(pVideoFrame);
  308. printf("---- exit video_decode_callback ----\n");
  309. return 0;
  310. }
  311. void video_image_display(struct play_info_t *play)
  312. {
  313. double delay,time;
  314. VideoPicture *vp;
  315. double diff,sync_threshold;
  316. if(play->pictq_size>0){
  317. vp=&play->pictq[play->pictq_rindex];
  318. delay=vp->pts - play->last_frame_pts;
  319. if(delay>0 && delay<10){
  320. play->last_frame_delay=delay;
  321. }
  322. delay=play->last_frame_delay;
  323. diff=vp->pts-get_audio_clock();
  324. sync_threshold = FFMAX(0.01, delay);
  325. if (fabs(diff) < 10) {
  326. if (diff <= -sync_threshold)
  327. delay = 0;
  328. else if (diff >= sync_threshold)
  329. delay = 2 * delay;
  330. }
  331. time=av_gettime()/1000000.0;
  332. if(time < play->frame_timer+delay)
  333. return ;
  334. if(delay>0)
  335. play->frame_timer+=delay;
  336. play->last_frame_pts=vp->pts;
  337. SDL_DisplayYUVOverlay(vp->bmp,&rect);
  338. play->pictq_rindex=(play->pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE;
  339. SDL_LockMutex(play->pictq_mutex);
  340. play->pictq_size--;
  341. SDL_CondSignal(play->pictq_cond);
  342. SDL_UnlockMutex(play->pictq_mutex);
  343. }
  344. }
  345. int SDLCALL video_show_callback(void* arg)
  346. {
  347. double delay=5000;
  348. struct play_info_t *play=(struct play_info_t*)arg;
  349. while(!quit){
  350. video_image_display(play);
  351. usleep(delay);
  352. }
  353. printf("----exit video_show_callback ----\n");
  354. return 0;
  355. }
  356. void player_log_callback(void* ptr, int level, const char* fmt, va_list vl)
  357. {
  358. static int print_prefix = 0;
  359. static int count;
  360. char line[1024];
  361. av_log_format_line(ptr, level, fmt, vl, line, sizeof(line), &print_prefix);
  362. printf("%s",line);
  363. }
  364. int main(int argc,char *argv[])
  365. {
  366. if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)<0){
  367. printf("Init SDL err!\n");
  368. return -1;
  369. }
  370. av_log_set_callback(player_log_callback);
  371. av_register_all();
  372. AVFormatContext *pFormatCtx;
  373. pFormatCtx=avformat_alloc_context();
  374. if(avformat_open_input(&pFormatCtx,argv[1],NULL,NULL)<0){
  375. printf("avformat_open_input err!\n");
  376. return -1;
  377. }
  378. if(avformat_find_stream_info(pFormatCtx,NULL)<0){
  379. printf("avformat_find_stream_info err!\n");
  380. return -1;
  381. }
  382. //av_dump_format(pFormatCtx,0,argv[1],0);
  383. int video_stream=-1,audio_stream=-1,i;
  384. for(i=0;i<pFormatCtx->nb_streams;i++){
  385. if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
  386. video_stream=i;
  387. }
  388. if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){
  389. audio_stream=i;
  390. }
  391. }
  392. if(video_stream==-1 || audio_stream==-1){
  393. printf("not find video or audio stream!\n");
  394. avformat_close_input(&pFormatCtx);
  395. return -1;
  396. }
  397. g_play_info.pVideoCodecCtx=pFormatCtx->streams[video_stream]->codec;
  398. g_play_info.pAudioCodecCtx=pFormatCtx->streams[audio_stream]->codec;
  399. //find the decoder
  400. AVCodec *pVideoCodec=avcodec_find_decoder(g_play_info.pVideoCodecCtx->codec_id);
  401. if(pVideoCodec==NULL){
  402. printf("not find video decoder!\n");
  403. avformat_close_input(&pFormatCtx);
  404. return -1;
  405. }
  406. AVCodec *pAudioCodec=avcodec_find_decoder(g_play_info.pAudioCodecCtx->codec_id);
  407. if(pVideoCodec==NULL){
  408. printf("not find audio decoder!\n");
  409. avformat_close_input(&pFormatCtx);
  410. return -1;
  411. }
  412. //open the codec
  413. if(avcodec_open(g_play_info.pVideoCodecCtx,pVideoCodec)<0){
  414. printf("can't open the video decoder!\n");
  415. avformat_close_input(&pFormatCtx);
  416. return -1;
  417. }
  418. if(avcodec_open(g_play_info.pAudioCodecCtx,pAudioCodec)<0){
  419. printf("can't open the audio decoder!\n");
  420. avcodec_close(g_play_info.pVideoCodecCtx);
  421. avformat_close_input(&pFormatCtx);
  422. return -1;
  423. }
  424. //setup SDL
  425. screen=SDL_SetVideoMode(PICTURE_W,PICTURE_H,0,0);
  426. picture_queque_init(&g_play_info);
  427. packet_queue_init(&video_queue);
  428. packet_queue_init(&audio_queue);
  429. rect.x=0;
  430. rect.y=0;
  431. rect.w=PICTURE_W;
  432. rect.h=PICTURE_H;
  433. //setup the sdl audio
  434. SDL_AudioSpec sdl_audio;
  435. int64_t wanted_channel_layout = 0;
  436. int wanted_nb_channels;
  437. wanted_channel_layout =
  438. (g_play_info.pAudioCodecCtx->channel_layout &&
  439. g_play_info.pAudioCodecCtx->channels == av_get_channel_layout_nb_channels(g_play_info.pAudioCodecCtx->channel_layout)) ? g_play_info.pAudioCodecCtx->channel_layout : av_get_default_channel_layout(g_play_info.pAudioCodecCtx->channels);
  440. wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
  441. wanted_nb_channels = av_get_channel_layout_nb_channels(wanted_channel_layout);
  442. /* SDL only supports 1, 2, 4 or 6 channels at the moment, so we have to make sure not to request anything else. */
  443. while (wanted_nb_channels > 0 && (wanted_nb_channels == 3 || wanted_nb_channels == 5 || wanted_nb_channels > 6)) {
  444. wanted_nb_channels--;
  445. wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
  446. }
  447. sdl_audio.channels=av_get_channel_layout_nb_channels(wanted_channel_layout);
  448. g_play_info.pAudioCodecCtx->channels=sdl_audio.channels;
  449. g_play_info.pAudioCodecCtx->channel_layout=wanted_channel_layout;
  450. g_play_info.audio_conv=swr_alloc_set_opts(NULL,wanted_channel_layout,AV_SAMPLE_FMT_S16,g_play_info.pAudioCodecCtx->sample_rate,
  451. wanted_channel_layout,g_play_info.pAudioCodecCtx->sample_fmt,
  452. g_play_info.pAudioCodecCtx->sample_rate,0,NULL);
  453. swr_init(g_play_info.audio_conv);
  454. sdl_audio.freq=g_play_info.pAudioCodecCtx->sample_rate;
  455. sdl_audio.format=AUDIO_S16SYS;
  456. sdl_audio.channels=g_play_info.pAudioCodecCtx->channels;
  457. sdl_audio.silence=0;
  458. sdl_audio.samples=1024;
  459. sdl_audio.callback=audio_callback;//calls when the audio device need more data
  460. sdl_audio.userdata=&g_play_info;
  461. if(SDL_OpenAudio(&sdl_audio,NULL)<0){
  462. printf("can't open audio device!\n");
  463. goto err_alloc_frame1;
  464. }
  465. g_play_info.vedio_clock=0;
  466. g_play_info.audio_clock=0;
  467. g_play_info.audio_current_pts=0;
  468. g_play_info.last_frame_pts=0;
  469. g_play_info.last_frame_delay=0;
  470. g_play_info.frame_timer=av_gettime()/1000000.0;
  471. g_play_info.pVideoCodecCtx->get_buffer=our_get_buffer;
  472. g_play_info.pVideoCodecCtx->release_buffer=our_release_buffer;
  473. SDL_PauseAudio(0);
  474. //create video decode thread
  475. //setup the converter
  476. g_play_info.video_conv=sws_getContext(g_play_info.pVideoCodecCtx->width,g_play_info.pVideoCodecCtx->height,
  477. g_play_info.pVideoCodecCtx->pix_fmt,
  478. PICTURE_PW,PICTURE_PH,PIX_FMT_YUYV422,SWS_POINT,NULL,NULL,NULL);
  479. SDL_Thread * video_decode_thread=SDL_CreateThread(video_decode_callback,(void*)&g_play_info);
  480. SDL_Thread * video_show_thread=SDL_CreateThread(video_show_callback,(void*)&g_play_info);
  481. //start to decode
  482. SDL_Event sdl_event;
  483. AVPacket packet;
  484. while(!quit){
  485. SDL_PollEvent(&sdl_event);
  486. if(sdl_event.type==SDL_QUIT){
  487. SDL_CondSignal(video_queue.cond);
  488. SDL_CondSignal(audio_queue.cond);
  489. quit=1;
  490. break;
  491. }
  492. if(video_queue.nb_packets>=MAX_QUEUE_SIZE || audio_queue.nb_packets>=MAX_QUEUE_SIZE){
  493. //printf("%d---%d\n",video_queue.nb_packets,audio_queue.nb_packets);
  494. usleep(10000);
  495. continue;
  496. }
  497. if(av_read_frame(pFormatCtx,&packet)<0){
  498. quit=1;
  499. continue;
  500. }
  501. if(packet.stream_index==video_stream){
  502. packet_queue_put(&video_queue,&packet);
  503. }else if(packet.stream_index==audio_stream){
  504. packet_queue_put(&audio_queue,&packet);
  505. }else{
  506. av_free_packet(&packet);
  507. }
  508. //usleep(5000);
  509. }
  510. SDL_CloseAudio();
  511. SDL_CondSignal(g_play_info.pictq_cond);
  512. SDL_WaitThread(video_decode_thread,NULL);
  513. SDL_WaitThread(video_show_thread,NULL);
  514. picture_queque_destroy(&g_play_info);
  515. swr_free(&g_play_info.audio_conv);
  516. sws_freeContext(g_play_info.video_conv);
  517. SDL_FreeSurface(screen);
  518. avcodec_close(g_play_info.pVideoCodecCtx);
  519. avcodec_close(g_play_info.pAudioCodecCtx);
  520. avformat_close_input(&pFormatCtx);
  521. return 0;
  522. err_alloc_frame1:
  523. avcodec_close(g_play_info.pAudioCodecCtx);
  524. avcodec_close(g_play_info.pVideoCodecCtx);
  525. avformat_close_input(&pFormatCtx);
  526. return -1;
  527. }
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include <stdio.h>
#include "SDL/SDL.h"
#include "SDL_thread.h"

#define PICTURE_W 720
#define PICTURE_H 576
#define PICTURE_PW 720
#define PICTURE_PH 576

#define MAX_QUEUE_SIZE 100
#define VIDEO_PICTURE_QUEUE_SIZE 2
typedef struct VideoPicture_t{
	SDL_Overlay *bmp;
	double pts;
	double duration;
	int width,height;
}VideoPicture;


struct play_info_t{
	double audio_clock;//解码时钟
	double audio_current_pts;//当前音频的pts
	double vedio_clock;//解码时钟
	double last_frame_pts;//上一帧的PTS值
	double last_frame_delay;//上一帧的延时
	double frame_timer;
	
	AVCodecContext *pVideoCodecCtx;
	AVCodecContext *pAudioCodecCtx;

	VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE];
	int pictq_size, pictq_rindex, pictq_windex;
    SDL_mutex *pictq_mutex;
    SDL_cond *pictq_cond;

	struct SwrContext *audio_conv;
	struct SwsContext *video_conv;
};

struct play_info_t g_play_info;




typedef struct PacketQueue {
	AVPacketList *first_pkt, *last_pkt;
	int nb_packets;
	int size;
	SDL_mutex *mutex;
	SDL_cond *cond;
} PacketQueue;

PacketQueue audio_queue;
PacketQueue video_queue;
int quit = 0;
SDL_Surface *screen;
SDL_Overlay *bmp;
SDL_Rect rect;

int global_video_pkt_pts=0;
int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) {
	int ret = avcodec_default_get_buffer(c, pic);
	uint64_t *pts = av_malloc(sizeof(uint64_t));
	*pts = global_video_pkt_pts;
	pic->opaque = pts;
	return ret;
}
void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) {
	if(pic) av_freep(&pic->opaque);
	avcodec_default_release_buffer(c, pic);
}

double get_audio_clock(void)
{
	return g_play_info.audio_current_pts;
}

double get_video_clock(void)
{
	return g_play_info.vedio_clock;
}

void picture_queque_init(struct play_info_t *play)
{
	int i;
	play->pictq_cond=SDL_CreateCond();
	play->pictq_mutex=SDL_CreateMutex();
	play->pictq_rindex=play->pictq_windex=0;
	play->pictq_size=0;
	memset(play->pictq,0,sizeof(play->pictq));
	for(i=0;i<VIDEO_PICTURE_QUEUE_SIZE;i++){
		play->pictq[i].bmp=SDL_CreateYUVOverlay(PICTURE_W,PICTURE_H,SDL_YUY2_OVERLAY,screen);
	}
}

void picture_queque_destroy(struct play_info_t *play)
{
	int i;
	for(i=0;i<VIDEO_PICTURE_QUEUE_SIZE;i++){
		SDL_FreeYUVOverlay(play->pictq[i].bmp);
	}
}



void packet_queue_init(PacketQueue *q) {
	memset(q, 0, sizeof(PacketQueue));
	q->mutex = SDL_CreateMutex();
	q->cond = SDL_CreateCond();
}

int packet_queue_put(PacketQueue *q, AVPacket *pkt) {
	AVPacketList *pkt1;
	if(av_dup_packet(pkt) < 0) {
		return -1;
	}
	pkt1 = av_malloc(sizeof(AVPacketList));
	if (!pkt1)
		return -1;
	pkt1->pkt = *pkt;
	pkt1->next = NULL;
	SDL_LockMutex(q->mutex);
	if (!q->last_pkt)
		q->first_pkt = pkt1;
	else
		q->last_pkt->next = pkt1;
	q->last_pkt = pkt1;
	q->nb_packets++;
	q->size += pkt1->pkt.size;
	SDL_CondSignal(q->cond);
	SDL_UnlockMutex(q->mutex);
	return 0;
}


static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {
	AVPacketList *pkt1;
	int ret;
	SDL_LockMutex(q->mutex);
	for(;;) {
		if(quit) {
			ret = -1;
			break;
		}
		pkt1 = q->first_pkt;
		if (pkt1) {
			q->first_pkt = pkt1->next;
			if (!q->first_pkt)
				q->last_pkt = NULL;
			q->nb_packets--;
			q->size -= pkt1->pkt.size;
			*pkt = pkt1->pkt;
			av_free(pkt1);
			ret = 1;
			break;
		} else if (!block) {
			ret = 0;
			break;
		} else {
			SDL_CondWait(q->cond, q->mutex);
		}
	}
	SDL_UnlockMutex(q->mutex);
	return ret;
}

int decode_interrupt_cb(void) {
	return quit;
}

//计算正确的pts值
double sync_video(struct play_info_t *play,AVFrame* frame,double pts)
{
	double frame_delay;
	if(pts!=0)
		play->vedio_clock=pts;
	else
		pts=play->vedio_clock;
	frame_delay=av_q2d(play->pVideoCodecCtx->time_base);//一帧占用的时间
	frame_delay+=frame->repeat_pict*(frame_delay*0.5);//计算重复帧
	play->vedio_clock+=frame_delay;
	return pts;
}

int audio_decode_frame(struct play_info_t* play, uint8_t *audio_buf,int buf_size) {
	AVCodecContext *aCodecCtx=play->pAudioCodecCtx;
	AVFrame *pAudioFrame=avcodec_alloc_frame();
	AVPacket pkt,pkt1;
	int frame_finished=0;
	int pkt_pos,pkt_len;
	int src_len=0,dst_len=0,data_size=0;
	float pts=0;
	avcodec_get_frame_defaults(pAudioFrame);

	uint8_t *out[]={audio_buf};
	
	for(;!quit;){
		if(packet_queue_get(&audio_queue, &pkt, 1) < 0) {
			av_free(pAudioFrame);
			return -1;
		}
		pkt1=pkt;
		pkt_pos=0;
		pkt_len=pkt.size;
		
		while(pkt_pos<pkt.size && !quit){
			if((src_len=avcodec_decode_audio4(aCodecCtx,pAudioFrame,&frame_finished,&pkt1))<0){
				av_free_packet(&pkt);
				av_free(pAudioFrame);
				return -1;
			}
			
			play->audio_clock+=(double)(pAudioFrame->linesize[0])/
				(aCodecCtx->channels*aCodecCtx->sample_rate*av_get_bytes_per_sample(aCodecCtx->sample_fmt));
			
			if(frame_finished){
				int len=swr_convert(play->audio_conv,out,buf_size/aCodecCtx->channels/av_get_bytes_per_sample(AV_SAMPLE_FMT_S16),
					pAudioFrame->data,pAudioFrame->linesize[0]/aCodecCtx->channels/av_get_bytes_per_sample(pAudioFrame->format));
				len=len*aCodecCtx->channels*av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
				av_free(pAudioFrame);
				av_free_packet(&pkt);
				return len;
			}else{
				if (!pkt1.data && aCodecCtx->codec->capabilities & CODEC_CAP_DELAY){
					break;
				}
			}
			pkt_pos+=src_len;//已经解码的长度
			pkt1.data=pkt.data+pkt_pos;
			pkt1.size=pkt.size-pkt_pos;
		}
		av_free_packet(&pkt);
	}
	av_free(pAudioFrame);
	return dst_len;
}

//decode the audio data ,and copy the result to stream
void SDLCALL audio_callback(void *userdata, Uint8 *stream, int len)
{
	struct play_info_t *play=(struct play_info_t*)userdata;
	AVCodecContext *aCodecCtx = play->pAudioCodecCtx;
	int len1, audio_size;
	static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
	static unsigned int audio_buf_size = 0;
	static unsigned int audio_buf_index = 0;
	int bytes_per_sec=2*aCodecCtx->channels*aCodecCtx->sample_rate;
	
	while(len > 0 && !quit) {
		if(audio_buf_index >= audio_buf_size) {
			audio_size = audio_decode_frame(play, audio_buf,sizeof(audio_buf));
			if(audio_size < 0) {
				audio_buf_size = 1024;
				memset(audio_buf, 0, audio_buf_size);
			} else {
				audio_buf_size = audio_size;
			}
			audio_buf_index = 0;
		}
		len1 = audio_buf_size - audio_buf_index;
		
		if(len1 > len)
			len1 = len;
		memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);
		len -= len1;
		stream += len1;
		audio_buf_index += len1;
	}
	if(audio_buf_size > audio_buf_index){
		play->audio_current_pts=play->audio_clock-((double)(audio_buf_size-audio_buf_index)/(double)bytes_per_sec);
		if(play->audio_current_pts<0)
			play->audio_current_pts=play->audio_clock;
		//printf("audio-pts:%f\n",play->audio_current_pts);
	}
}

int SDLCALL video_decode_callback(void* arg)
{
	printf("---in video decode thread ---\n");
	struct play_info_t* play=(struct play_info_t*)arg;
	AVCodecContext *pVideoCodecCtx=play->pVideoCodecCtx;
	double pts;
	VideoPicture *vp;
	AVPacket pkt;
	int frame_finished;
	AVPicture pict = { { 0 } };
	AVFrame *pVideoFrame=avcodec_alloc_frame();
	if(pVideoFrame==NULL){
		printf("can't alloc video frame!\n");
		return -1;
	}

	while(!quit){
		if(packet_queue_get(&video_queue, &pkt, 1) < 0) {
			return -1;
		}
		pts = 0;
		// Save global pts to be stored in pFrame in first call
		global_video_pkt_pts = pkt.pts;
		avcodec_decode_video2(pVideoCodecCtx,pVideoFrame,&frame_finished,&pkt);
		if(pkt.dts == AV_NOPTS_VALUE && pVideoFrame->opaque && *(uint64_t*)pVideoFrame->opaque != AV_NOPTS_VALUE){
			pts = *(uint64_t *)pVideoFrame->opaque;
		} else if(pkt.dts!=AV_NOPTS_VALUE){
			pts=pkt.dts;
		}else{
			pts=0;
		}
		pts*=av_q2d(pVideoCodecCtx->time_base);
	
		av_free_packet(&pkt);
		if(frame_finished){
			SDL_LockMutex(play->pictq_mutex);
			while(play->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !quit){
				SDL_CondWait(play->pictq_cond,play->pictq_mutex);
			}
			SDL_UnlockMutex(play->pictq_mutex);
			if(quit)
				goto out;
			
			vp=&play->pictq[play->pictq_windex];
			SDL_Overlay *bmp=vp->bmp;
			//序列化帧
			pts=sync_video(&g_play_info,pVideoFrame,pts);
			vp->pts=pts;
			//printf("video-pts:%f\n",pts);
			
			SDL_LockYUVOverlay(bmp);
			pict.data[0]=bmp->pixels[0];
			pict.data[1]=bmp->pixels[2];
			pict.data[2]=bmp->pixels[1];
			pict.linesize[0]=bmp->pitches[0];
			pict.linesize[1]=bmp->pitches[2];
			pict.linesize[2]=bmp->pitches[1];
			sws_scale(play->video_conv,pVideoFrame->data,pVideoFrame->linesize,0,
				pVideoFrame->height,pict.data,pict.linesize);
			SDL_UnlockYUVOverlay(bmp);


			play->pictq_windex=(play->pictq_windex+1)%VIDEO_PICTURE_QUEUE_SIZE;
			SDL_LockMutex(play->pictq_mutex);
			play->pictq_size++;
			SDL_UnlockMutex(play->pictq_mutex);
			
		}
	}
out:
	av_free(pVideoFrame);
	printf("---- exit video_decode_callback ----\n");
	return 0;
}

void video_image_display(struct play_info_t *play)
{
	double delay,time;
	VideoPicture *vp;
	double diff,sync_threshold;
	if(play->pictq_size>0){
		vp=&play->pictq[play->pictq_rindex];
		delay=vp->pts - play->last_frame_pts;
		if(delay>0 && delay<10){
			play->last_frame_delay=delay;
		}
		delay=play->last_frame_delay;
		
		diff=vp->pts-get_audio_clock();
		sync_threshold = FFMAX(0.01, delay);
		if (fabs(diff) < 10) {
            if (diff <= -sync_threshold)
                delay = 0;
            else if (diff >= sync_threshold)
                delay = 2 * delay;
        }

		time=av_gettime()/1000000.0;
		if(time < play->frame_timer+delay)
			return ;
		
		if(delay>0)
			play->frame_timer+=delay;
		play->last_frame_pts=vp->pts;

		SDL_DisplayYUVOverlay(vp->bmp,&rect);

		play->pictq_rindex=(play->pictq_rindex+1)%VIDEO_PICTURE_QUEUE_SIZE;

		SDL_LockMutex(play->pictq_mutex);
		play->pictq_size--;
		SDL_CondSignal(play->pictq_cond);
		SDL_UnlockMutex(play->pictq_mutex);
		
	}
}

int SDLCALL video_show_callback(void* arg)
{
	double delay=5000;
	struct play_info_t *play=(struct play_info_t*)arg;
	while(!quit){
		video_image_display(play);
		usleep(delay);
	}
	printf("----exit video_show_callback ----\n");
	return 0;
}

void player_log_callback(void* ptr, int level, const char* fmt, va_list vl)
{
    static int print_prefix = 0;
    static int count;
    char line[1024];
    av_log_format_line(ptr, level, fmt, vl, line, sizeof(line), &print_prefix);
    printf("%s",line);
}




int main(int argc,char *argv[])
{
	if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)<0){
		printf("Init SDL err!\n");
		return -1;
	}

	av_log_set_callback(player_log_callback);
	av_register_all();

	AVFormatContext *pFormatCtx;
	pFormatCtx=avformat_alloc_context();
	if(avformat_open_input(&pFormatCtx,argv[1],NULL,NULL)<0){
		printf("avformat_open_input err!\n");
		return -1;
	}

	if(avformat_find_stream_info(pFormatCtx,NULL)<0){
		printf("avformat_find_stream_info err!\n");
		return -1;
	}

	//av_dump_format(pFormatCtx,0,argv[1],0);
	
	int video_stream=-1,audio_stream=-1,i;
	for(i=0;i<pFormatCtx->nb_streams;i++){
		if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
			video_stream=i;
		}
		if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){
			audio_stream=i;
		}
	}
	if(video_stream==-1 || audio_stream==-1){
		printf("not find video or audio stream!\n");
		avformat_close_input(&pFormatCtx);
		return -1;
	}
	g_play_info.pVideoCodecCtx=pFormatCtx->streams[video_stream]->codec;
	g_play_info.pAudioCodecCtx=pFormatCtx->streams[audio_stream]->codec;

	//find the decoder
	AVCodec *pVideoCodec=avcodec_find_decoder(g_play_info.pVideoCodecCtx->codec_id);
	if(pVideoCodec==NULL){
		printf("not find video decoder!\n");
		avformat_close_input(&pFormatCtx);
		return -1;
	}
	AVCodec *pAudioCodec=avcodec_find_decoder(g_play_info.pAudioCodecCtx->codec_id);
	if(pVideoCodec==NULL){
		printf("not find audio decoder!\n");
		avformat_close_input(&pFormatCtx);
		return -1;
	}
	//open the codec
	if(avcodec_open(g_play_info.pVideoCodecCtx,pVideoCodec)<0){
		printf("can't open the video decoder!\n");
		avformat_close_input(&pFormatCtx);
		return -1;
	}
	if(avcodec_open(g_play_info.pAudioCodecCtx,pAudioCodec)<0){
		printf("can't open the audio decoder!\n");
		avcodec_close(g_play_info.pVideoCodecCtx);
		avformat_close_input(&pFormatCtx);
		return -1;
	}

	//setup SDL
	screen=SDL_SetVideoMode(PICTURE_W,PICTURE_H,0,0);
	picture_queque_init(&g_play_info);
	packet_queue_init(&video_queue);
	packet_queue_init(&audio_queue);
	
	rect.x=0;
	rect.y=0;
	rect.w=PICTURE_W;
	rect.h=PICTURE_H;


	//setup the sdl audio
	SDL_AudioSpec sdl_audio;
	int64_t wanted_channel_layout = 0;
    int wanted_nb_channels;
	wanted_channel_layout = 
		(g_play_info.pAudioCodecCtx->channel_layout && 
		g_play_info.pAudioCodecCtx->channels == av_get_channel_layout_nb_channels(g_play_info.pAudioCodecCtx->channel_layout)) ? g_play_info.pAudioCodecCtx->channel_layout : av_get_default_channel_layout(g_play_info.pAudioCodecCtx->channels);
    wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
    wanted_nb_channels = av_get_channel_layout_nb_channels(wanted_channel_layout);
    /* SDL only supports 1, 2, 4 or 6 channels at the moment, so we have to make sure not to request anything else. */
    while (wanted_nb_channels > 0 && (wanted_nb_channels == 3 || wanted_nb_channels == 5 || wanted_nb_channels > 6)) {
        wanted_nb_channels--;
        wanted_channel_layout = av_get_default_channel_layout(wanted_nb_channels);
    }
	sdl_audio.channels=av_get_channel_layout_nb_channels(wanted_channel_layout);
	g_play_info.pAudioCodecCtx->channels=sdl_audio.channels;
	g_play_info.pAudioCodecCtx->channel_layout=wanted_channel_layout;
	
	g_play_info.audio_conv=swr_alloc_set_opts(NULL,wanted_channel_layout,AV_SAMPLE_FMT_S16,g_play_info.pAudioCodecCtx->sample_rate,
		wanted_channel_layout,g_play_info.pAudioCodecCtx->sample_fmt,
		g_play_info.pAudioCodecCtx->sample_rate,0,NULL);
	swr_init(g_play_info.audio_conv);
	
	sdl_audio.freq=g_play_info.pAudioCodecCtx->sample_rate;
	sdl_audio.format=AUDIO_S16SYS;
	sdl_audio.channels=g_play_info.pAudioCodecCtx->channels;
	sdl_audio.silence=0;
	sdl_audio.samples=1024;
	sdl_audio.callback=audio_callback;//calls when the audio device need more data
	sdl_audio.userdata=&g_play_info;
	if(SDL_OpenAudio(&sdl_audio,NULL)<0){
		printf("can't open audio device!\n");
		goto err_alloc_frame1;
	}

	g_play_info.vedio_clock=0;
	g_play_info.audio_clock=0;
	g_play_info.audio_current_pts=0;
	g_play_info.last_frame_pts=0;
	g_play_info.last_frame_delay=0;
	g_play_info.frame_timer=av_gettime()/1000000.0;
	g_play_info.pVideoCodecCtx->get_buffer=our_get_buffer;
	g_play_info.pVideoCodecCtx->release_buffer=our_release_buffer;
	
	SDL_PauseAudio(0);

	//create video decode thread
		//setup the converter
	
	g_play_info.video_conv=sws_getContext(g_play_info.pVideoCodecCtx->width,g_play_info.pVideoCodecCtx->height,
		g_play_info.pVideoCodecCtx->pix_fmt,
		PICTURE_PW,PICTURE_PH,PIX_FMT_YUYV422,SWS_POINT,NULL,NULL,NULL);
	SDL_Thread * video_decode_thread=SDL_CreateThread(video_decode_callback,(void*)&g_play_info);
	SDL_Thread * video_show_thread=SDL_CreateThread(video_show_callback,(void*)&g_play_info);
	//start to decode
	SDL_Event sdl_event;
	AVPacket packet;


	
	while(!quit){
		SDL_PollEvent(&sdl_event);
		if(sdl_event.type==SDL_QUIT){
			SDL_CondSignal(video_queue.cond);
			SDL_CondSignal(audio_queue.cond);
			quit=1;
			break;
		}
		if(video_queue.nb_packets>=MAX_QUEUE_SIZE || audio_queue.nb_packets>=MAX_QUEUE_SIZE){
			//printf("%d---%d\n",video_queue.nb_packets,audio_queue.nb_packets);
			usleep(10000);
			continue;
		}
		if(av_read_frame(pFormatCtx,&packet)<0){
			quit=1;
			continue;
		}
		if(packet.stream_index==video_stream){
			packet_queue_put(&video_queue,&packet);
		}else if(packet.stream_index==audio_stream){
			packet_queue_put(&audio_queue,&packet);
		}else{
			av_free_packet(&packet);
		}
		
		//usleep(5000);
	}
	SDL_CloseAudio();
	SDL_CondSignal(g_play_info.pictq_cond);
	SDL_WaitThread(video_decode_thread,NULL);
	SDL_WaitThread(video_show_thread,NULL);
	picture_queque_destroy(&g_play_info);

	swr_free(&g_play_info.audio_conv);
	sws_freeContext(g_play_info.video_conv);

	SDL_FreeSurface(screen);
	avcodec_close(g_play_info.pVideoCodecCtx);
	avcodec_close(g_play_info.pAudioCodecCtx);
	avformat_close_input(&pFormatCtx);
	return 0;

err_alloc_frame1:
	avcodec_close(g_play_info.pAudioCodecCtx);
	avcodec_close(g_play_info.pVideoCodecCtx);
	avformat_close_input(&pFormatCtx);
	return -1;
}

使用ffmpeg+SDL的简单播放器 - redtea - 熠彩网络

  评论这张
 
阅读(1127)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017