ffmpeg+sdl教程----编写一个简单的播放器3(为视频加入音频)

来源:http://blog.csdn.net/mu399/article/details/5814901

上个教程实现了视频的简单播放,但那是个哑巴电影,完全没有声音。

这个教程第一次用到了SDL的线程,涉及到了两个线程间的同步协调,有几个地方需要特别留意,SDL_OpenAudio库函数会打开音频设备(0是恢 复,其他的是暂停),SDL_PauseAudio库函数可以暂停或者恢复audio_callback函数的执行,程序中的这行代码 “SDL_PauseAudio(0);”执行后,让audio_callback函数开始反复的被调用。在这之前audio_callback回调函数 还没有被调用。

audio_callback函数

原型为void callback(void *userdata, Uint8 *stream, int len),userdata是输入,stream是输出,len是输入,len的值一般为4096(调试中发现的),audio_callback函数的 功能是调用audio_decode_frame函数,把解码后数据块audio_buf追加在stream的后面,通过SDL库对 audio_callback的不断调用,不断解码数据,然后放到stream的末尾,SDL库认为stream中数据够播放一帧音频了,就播放它,第三 个参数len是向stream中写数据的内存分配尺度,是分配给audio_callback函数写入缓存大小。

假设len=4096,解码后数据块audio_buf的大小为4608,那么一次audio_callback调用不能把audio_buf中全部数据 写入stream末尾,就分两次,第一次先把audio_buf的前4096个字节写入stream末尾,第二次调用audio_callback函数 时,由于写缓存用光了,又分配4096个字节的缓存,再写剩余的512个字节到stream末尾,写缓存还剩余3584个字节留给下次 audio_callback调用使用。

audio_decode_frame函数

原型:int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size)

返回值:解码完一帧音频到缓存后,缓存占用的实际大小,以字节为单位,为负数表示失败

aCodecCtx:输入,解码上下文

audio_buf:输出,解码成功后,输出到的缓存的首地址

buf_size:输入,audio_buf的预留空间

该函数是实际上是从尾部开始执行的,先取得main线程放入队列的包,再用库函数avcodec_decode_audio2处理,如果一次调用没有处理 完一个包的数据,记录下处理到包的那个位置了,下次接着处理(这种情况可能是因为一个音频包,包含多个音频帧的数据引起)

库函数avcodec_decode_audio2

原型:int avcodec_decode_audio2(AVCodecContext *avctx, int16_t *samples,
                         int *frame_size_ptr,
                         const uint8_t *buf, int buf_size);
    avctx : 解码器上下文
    samples: 输出参数  输出数据的缓存首地址.
    frame_size_ptr:既是输入又是输出,无帧可解返回0,解码失败返回负数,解码成功返回,解码后一帧音频所占空间,以字节为单位
    buf: 输入参数,输入数据的缓存
    buf_size:输入参数,buf的大小
    返回值:无帧可解返回0,解码失败返回负数,解码成功返回解码前一帧音频所占空间

SDL_CondWait库函数

等待消息时解锁,等到消息后加锁,该函数可以阻塞代码的执行,一般和SDL_CondSignal库函数(或SDL_CondBroadcast库函数)配对使用

[cpp] view plaincopy

  1. // ffmpegExe.cpp: 主项目文件。
  2. #include "libavformat/avformat.h"
  3. #include "libswscale/swscale.h"
  4. #include <windows.h>
  5. #include <stdlib.h>
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <math.h>
  9. #include <SDL/SDL.h>
  10. #include <SDL/SDL_thread.h>
  11. #ifdef main
  12. #undef main
  13. #endif
  14. #define SDL_AUDIO_BUFFER_SIZE 1024
  15. static int sws_flags = SWS_BICUBIC;
  16. typedef struct PacketQueue
  17. {
  18. AVPacketList *first_pkt, *last_pkt;
  19. int nb_packets;
  20. int size;
  21. SDL_mutex *mutex;
  22. SDL_cond *cond;
  23. } PacketQueue;
  24. PacketQueue audioq;
  25. int quit = 0;
  26. void packet_queue_init(PacketQueue *q)
  27. {
  28. memset(q, 0, sizeof(PacketQueue));
  29. q->mutex = SDL_CreateMutex();
  30. q->cond = SDL_CreateCond();
  31. }
  32. int packet_queue_put(PacketQueue *q, AVPacket *pkt)
  33. {
  34. AVPacketList *pkt1;
  35. if(av_dup_packet(pkt) < 0)
  36. {
  37. return -1;
  38. }
  39. pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));
  40. if (!pkt1)
  41. return -1;
  42. pkt1->pkt = *pkt;
  43. pkt1->next = NULL;
  44. SDL_LockMutex(q->mutex);
  45. if (!q->last_pkt)
  46. q->first_pkt = pkt1;
  47. else
  48. q->last_pkt->next = pkt1;
  49. q->last_pkt = pkt1;
  50. q->nb_packets++;
  51. q->size += pkt1->pkt.size;
  52. SDL_CondSignal(q->cond);
  53. SDL_UnlockMutex(q->mutex);
  54. return 0;
  55. }
  56. static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
  57. {
  58. AVPacketList *pkt1;
  59. int ret;
  60. SDL_LockMutex(q->mutex);
  61. for(;;)
  62. {
  63. if(quit)
  64. {
  65. ret = -1;
  66. break;
  67. }
  68. pkt1 = q->first_pkt;
  69. if (pkt1)
  70. {
  71. q->first_pkt = pkt1->next;
  72. if (!q->first_pkt)
  73. q->last_pkt = NULL;
  74. q->nb_packets--;
  75. q->size -= pkt1->pkt.size;
  76. *pkt = pkt1->pkt;
  77. av_free(pkt1);
  78. ret = 1;
  79. break;
  80. }
  81. else if (!block)
  82. {
  83. ret = 0;
  84. break;
  85. }
  86. else
  87. {
  88. SDL_CondWait(q->cond, q->mutex);
  89. }
  90. }
  91. SDL_UnlockMutex(q->mutex);
  92. return ret;
  93. }
  94. int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size)
  95. {
  96. static AVPacket pkt;
  97. static uint8_t *audio_pkt_data = NULL;
  98. static int audio_pkt_size = 0;
  99. int len1, data_size;
  100. for(;;)
  101. {
  102. while(audio_pkt_size > 0)
  103. {
  104. data_size = buf_size;
  105. len1 = avcodec_decode_audio2(aCodecCtx, (int16_t *)audio_buf, &data_size, audio_pkt_data, audio_pkt_size);
  106. if(len1 < 0)
  107. { /* if error, skip frame */
  108. audio_pkt_size = 0;
  109. break;
  110. }
  111. audio_pkt_data += len1;
  112. audio_pkt_size -= len1;
  113. if(data_size <= 0)
  114. { /* No data yet, get more frames */
  115. continue;
  116. } /* We have data, return it and come back for more later */
  117. return data_size;
  118. }
  119. if(pkt.data)
  120. av_free_packet(&pkt);
  121. if(quit)
  122. {
  123. return -1;
  124. }
  125. if(packet_queue_get(&audioq, &pkt, 1) < 0)
  126. {
  127. return -1;
  128. }
  129. audio_pkt_data = pkt.data;
  130. audio_pkt_size = pkt.size;
  131. }
  132. }
  133. void audio_callback(void *userdata, Uint8 *stream, int len)
  134. {
  135. AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;
  136. int len1, audio_size;
  137. static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
  138. static unsigned int audio_buf_size = 0;
  139. static unsigned int audio_buf_index = 0;
  140. while(len > 0)
  141. {
  142. if(audio_buf_index >= audio_buf_size)
  143. { /* We have already sent all our data; get more */
  144. audio_size = audio_decode_frame(aCodecCtx, audio_buf, sizeof(audio_buf));
  145. if(audio_size < 0)
  146. { /* If error, output silence */
  147. audio_buf_size = 1024; // arbitrary?
  148. memset(audio_buf, 0, audio_buf_size);
  149. }
  150. else
  151. {
  152. audio_buf_size = audio_size;
  153. }
  154. audio_buf_index = 0;
  155. }
  156. len1 = audio_buf_size - audio_buf_index;
  157. if(len1 > len)
  158. len1 = len;
  159. memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);
  160. len -= len1;
  161. stream += len1;
  162. audio_buf_index += len1;
  163. }
  164. }
  165. int main(int argc, char *argv[])
  166. {
  167. AVFormatContext *pFormatCtx;
  168. int i, videoStream(-1), audioStream(-1);
  169. AVCodecContext *pCodecCtx;
  170. AVCodec *pCodec;
  171. AVFrame *pFrame;
  172. AVPacket packet;
  173. int frameFinished;
  174. float aspect_ratio;
  175. AVCodecContext *aCodecCtx;
  176. AVCodec *aCodec;
  177. SDL_Overlay *bmp;
  178. SDL_Surface *screen;
  179. SDL_Rect rect;
  180. SDL_Event event;
  181. SDL_AudioSpec wanted_spec, spec;
  182. if(argc < 2)
  183. {
  184. fprintf(stderr, "Usage: test /n");
  185. exit(1);
  186. }
  187. av_register_all();
  188. pFormatCtx = av_alloc_format_context();
  189. if (!pFormatCtx) {
  190. fprintf(stderr, "Memory error/n");
  191. exit(1);
  192. }
  193. if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
  194. return -1; // Couldn‘t open file
  195. if(av_find_stream_info(pFormatCtx)<0)
  196. return -1; // Couldn‘t find stream information
  197. // Dump information about file onto standard error
  198. dump_format(pFormatCtx, 0, argv[1], 0);
  199. // Find the first video stream
  200. for(i=0; i<pFormatCtx->nb_streams; i++)
  201. {
  202. if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO && videoStream<0)
  203. {
  204. videoStream=i;
  205. }
  206. if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO && audioStream<0)
  207. {
  208. audioStream=i;
  209. }
  210. }
  211. if(videoStream==-1||audioStream==-1)
  212. return -1; // Didn‘t find a video stream
  213. // Get a pointer to the codec context for the video stream
  214. aCodecCtx=pFormatCtx->streams[audioStream]->codec;
  215. wanted_spec.freq = aCodecCtx->sample_rate;
  216. wanted_spec.format = AUDIO_S16SYS;
  217. wanted_spec.channels = aCodecCtx->channels;
  218. wanted_spec.silence = 0;
  219. wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
  220. wanted_spec.callback = audio_callback;
  221. wanted_spec.userdata = aCodecCtx;
  222. if(SDL_OpenAudio(&wanted_spec, &spec) < 0)
  223. {
  224. fprintf(stderr, "SDL_OpenAudio: %s/n", SDL_GetError());
  225. return -1;
  226. }
  227. aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
  228. if(!aCodec)
  229. {
  230. fprintf(stderr, "Unsupported codec!/n"); return -1;
  231. }
  232. avcodec_open(aCodecCtx, aCodec); // audio_st = pFormatCtx->streams[index]
  233. packet_queue_init(&audioq);
  234. SDL_PauseAudio(0);
  235. pCodecCtx=pFormatCtx->streams[videoStream]->codec;
  236. pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
  237. if(pCodec==NULL)
  238. {
  239. fprintf(stderr, "Unsupported codec!/n");
  240. return -1; // Codec not found
  241. }
  242. // Open codec
  243. if(avcodec_open(pCodecCtx, pCodec)<0)
  244. return -1; // Could not open codec
  245. // Allocate video frame
  246. pFrame=avcodec_alloc_frame();
  247. // Allocate an AVFrame structure
  248. uint8_t *buffer;
  249. int numBytes;
  250. // Determine required buffer size and allocate buffer
  251. numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
  252. pCodecCtx->height);
  253. buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
  254. // Assign appropriate parts of buffer to image planes in pFrameRGB
  255. if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
  256. {
  257. fprintf(stderr, "Could not initialize SDL - %s/n", SDL_GetError());
  258. exit(1);
  259. }
  260. #ifndef __DARWIN__
  261. screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);
  262. #else
  263. screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0);
  264. #endif
  265. if(!screen)
  266. {
  267. fprintf(stderr, "SDL: could not set video mode - exiting/n");
  268. exit(1);
  269. }
  270. bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,
  271. SDL_YV12_OVERLAY, screen);
  272. static struct SwsContext *img_convert_ctx;
  273. if (img_convert_ctx == NULL)
  274. {
  275. img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
  276. pCodecCtx->pix_fmt,
  277. pCodecCtx->width, pCodecCtx->height,
  278. PIX_FMT_YUV420P,
  279. sws_flags, NULL, NULL, NULL);
  280. if (img_convert_ctx == NULL)
  281. {
  282. fprintf(stderr, "Cannot initialize the conversion context/n");
  283. exit(1);
  284. }
  285. }
  286. i=0;
  287. while(av_read_frame(pFormatCtx, &packet)>=0)
  288. {
  289. // Is this a packet from the video stream?
  290. if(packet.stream_index==videoStream)
  291. {
  292. // Decode video frame
  293. avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
  294. packet.data, packet.size);
  295. // Did we get a video frame?
  296. if(frameFinished)
  297. {
  298. SDL_LockYUVOverlay(bmp);
  299. AVPicture pict;
  300. pict.data[0] = bmp->pixels[0];
  301. pict.data[1] = bmp->pixels[2];
  302. pict.data[2] = bmp->pixels[1];
  303. pict.linesize[0] = bmp->pitches[0];
  304. pict.linesize[1] = bmp->pitches[2];
  305. pict.linesize[2] = bmp->pitches[1];
  306. // Convert the image into YUV format that SDL uses
  307. /*img_convert(&pict, PIX_FMT_YUV420P,
  308. (AVPicture *)pFrame, pCodecCtx->pix_fmt,
  309. pCodecCtx->width, pCodecCtx->height);*/
  310. sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize,
  311. 0, pCodecCtx->height, pict.data, pict.linesize);
  312. SDL_UnlockYUVOverlay(bmp);
  313. rect.x = 0;
  314. rect.y = 0;
  315. rect.w = pCodecCtx->width;
  316. rect.h = pCodecCtx->height;
  317. SDL_DisplayYUVOverlay(bmp, &rect);
  318. Sleep(60);
  319. av_free_packet(&packet);
  320. }
  321. }
  322. else if(packet.stream_index==audioStream)
  323. {
  324. packet_queue_put(&audioq, &packet);
  325. }
  326. else
  327. {
  328. av_free_packet(&packet);
  329. }
  330. // Free the packet that was allocated by av_read_frame
  331. SDL_PollEvent(&event);
  332. switch(event.type)
  333. {
  334. case SDL_QUIT:
  335. quit = 1;
  336. SDL_Quit();
  337. exit(0);
  338. break;
  339. default: break;
  340. }
  341. }
  342. // Free the RGB image
  343. av_free(buffer);
  344. //av_free(pFrameRGB);
  345. // Free the YUV frame
  346. av_free(pFrame);
  347. // Close the codec
  348. avcodec_close(pCodecCtx);
  349. // Close the video file
  350. av_close_input_file(pFormatCtx);
  351. return 0;
  352. }
时间: 2024-12-29 11:29:51

ffmpeg+sdl教程----编写一个简单的播放器3(为视频加入音频)的相关文章

ffmpeg+sdl教程----编写一个简单的播放器5(同步视频到音频)

来源:http://blog.csdn.net/mu399/article/details/5816566 个人认为,这这部分教程的新增代码量虽然不是最多的,难度却是最大的,重复看了多次才明白,因为有两个问题的困扰,搞得还不清楚: 1.音频和视频既然都有各自的时间戳,各自按各自的时间戳来播放不就行了,为什么还需要同步呢? 2.如果要把视频同步到音频,怎么同步?或者说以什么标准来同步? 第一个问题的答案可能是,一是音频和视频的开始播放的时间是不一样,二是播放每帧音频或视频时可能必须把解码数据,视频

ffmpeg+sdl教程----编写一个简单的播放器2(输出视频到屏幕)

来源:http://blog.csdn.net/mu399/article/details/5814859 下面完整代码,在vc2005下编译通过.可以看到,程序运行后视频播放出来了,但是由于没有加入播放延迟,视频简直跑疯了,为视频加入延迟将在教程五中实现,目前可以简单地让程序在播放完一帧后,sleep若干秒,改善一下运行状况. [cpp] view plaincopy // ffmpegExe.cpp: 主项目文件. #include "stdafx.h" #include &quo

ffmpeg+sdl教程----编写一个简单的播放器4(让程序更模块化)

来源:http://blog.csdn.net/mu399/article/details/5815444 音频和视频之间的同步,再那之前需要先做一些准备工作. 为了让程序更模块化,便于扩展,需要把原来main函数中的各个功能模块代码分离出来放在相应的函数中.该教程和上个教程相比代码量和难度都增加很多,比上个教程使用了更多的线程,一定要理解清楚各个函数和数据结构之间的关联以及线程之间如何协同工作. [c-sharp] view plaincopy // ffmpegExe.cpp: 主项目文件.

ffmpeg+sdl教程----编写一个简单的播放器6(其他的时钟同步方式)

来源:http://blog.csdn.net/mu399/article/details/5818384 在理解上一个教程的基础上,这篇教程就稍微容易理解些了,不外乎多加了两种同步方式,同步音频到视频,同步音频视频到外部时钟. 这篇教程主要是新增了不少新变量,is->video_current_pts用于保存当前视频帧的时间戳(以秒为单位),只在 video_refresh_timer函数中播放一帧视频前改变,is->video_current_pts_time单位为毫秒,在 stream_

ffmpeg+sdl教程----编写一个简单的播放器7(处理快进快退命令)

来源:http://blog.csdn.net/mu399/article/details/5818970 这篇教程例子中的程序,让右方向按键为快进10秒,上方向按键为快进60秒,左方向按键为快退10秒,上方向按键为快退60秒,程序中的 av_seek_frame函数可能是用错了,或者函数本身的问题导致按上和右都没反应;按左和下让画面暂停,声音在很短区间内不停播放,这时再按右和下 才正常. [cpp] view plaincopy #include "libavformat/avformat.h

可视化程序设计基础(三)——一个简单的播放器(并不)

本次的作业是制作一个简单的播放器,功能仅限于播放视频和音频,虽说是简单的播放器,但其中还是有很多细节需要注意的. 问题一:布局 本来这个问题不应该是一个问题了,之前老师讲过的Stackpanel和Grid等对于布局一个播放器来说绰绰有余,但上次上课老师提到的NavigationView令我十分感兴趣,这是一个uwp应用程序中随处可见的一种布局,节省了开发者很多的时间. 所以我就着手于建立这个NavigationView了,首先我看了一下XAML Controls Gallery,然而其中关于Na

[SimplePlayer] 实现一个简单的播放器

简单的播放器需要实现一个最基本的功能:播放视频文件. 实现这个功能需要包含以下几个步骤: 从视频文件中提取视频图像 在屏幕上显示视频图像 视频帧的同步,也就是保证视频图像在合适的时间在屏幕上显示 从视频文件中提取音频 向音频设备输出音频 音频同步,也就是保证合适的时间输出合适的音频 多线程处理 音视频同步 本实现是通过ffmpeg来实现音视频的提取,通过sdl2来实现音视频的输出,版本如下: libavutil 56. 19.100 / 56. 19.100 libavcodec 58. 23.

qt实现一个简单的播放器

ui界面设计如下: 2.代码结构 3.dialog.cpp #include "dialog.h" #include "ui_dialog.h" #include<QStringList> #include<QDebug> Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) { ui->setupUi(this); ui->lb_dis->

Android开发6:Service的使用(简单音乐播放器的实现)

前言 啦啦啦~各位好久不见啦~博主最近比较忙,而且最近一次实验也是刚刚结束~ 好了不废话了,直接进入我们这次的内容~ 在这篇博文里我们将学习Service(服务)的相关知识,学会使用 Service 进行后台工作, 学会使用 Service 与 Activity 进行通信,并在此知识基础上学会使用 MediaPlayer和简单的多线程编程.使用 Handle 更新 UI,并设计成功一个简单的音乐播放器. 是不是很高大上呢~一起来学习~ 基础知识 Service作为Android四大组件之一,在每