目录 [hide]
抽象流程:
设置SDL的音频参数 —-> 打开声音设备,播放静音 —-> ffmpeg读取音频流中数据放入队列 —-> SDL调用用户设置的函数来获取音频数据 —-> 播放音频
SDL内部维护了一个buffer来存放解码后的数据,这个buffer中的数据来源是我们注册的回调函数(audio_callback),audio_callback调用audio_decode_frame来做具体的音频解码工作,需要引起注意的是:从流中读取出的一个音频包(avpacket)可能含有多个音频桢(avframe),所以需要多次调用avcodec_decode_audio4来完成整个包的解码,解码出来的数据存放在我们自己的缓冲中(audio_buf2)。SDL每一次回调都会引起数据从audio_buf2拷贝到SDL内部缓冲区,当audio_buf2中的数据大于SDL的缓冲区大小时,需要分多次拷贝。
关键实现:
main()函数
1 |
int main(int argc, char **argv){ |
2 |
SDL_Event event; //SDL事件变量 |
3 |
VideoState *is; // 纪录视频及解码器等信息的大结构体 |
4 |
is = (VideoState*) av_mallocz(sizeof(VideoState)); |
6 |
fprintf(stderr, "Usage: play <file>\n"); |
9 |
av_register_all(); //注册所有ffmpeg的解码器 |
10 |
/* 初始化SDL,这里只实用了AUDIO,如果有视频,好需要SDL_INIT_VIDEO等等 */ |
11 |
if(SDL_Init(SDL_INIT_AUDIO)){ |
12 |
fprintf(stderr, "Count not initialize SDL - %s\n", SDL_GetError()); |
15 |
is_strlcpy(is->filename, argv[1], sizeof(is->filename)); |
16 |
/* 创建一个SDL线程来做视频解码工作,主线程进入SDL事件循环 */ |
17 |
is->parse_tid = SDL_CreateThread(decode_thread, is); |
19 |
SDL_WaitEvent(&event); |
decode_thread()读取文件信息和音频包
1 |
static int decode_thread(void *arg){ |
2 |
VideoState *is = (VideoState*)arg; |
3 |
AVFormatContext *ic = NULL; |
4 |
AVPacket pkt1, *packet = &pkt1; |
5 |
int ret, i, audio_index = -1; |
8 |
global_video_state = is; |
9 |
/* 使用ffmpeg打开视频,解码器等 常规工作 */ |
10 |
if(avFormat_open_input(&ic, is->filename, NULL, NULL) != 0) { |
11 |
fprintf(stderr, "open file error: %s\n", is->filename); |
15 |
if(avformat_find_stream_info(ic, NULL) < 0){ |
16 |
fprintf(stderr, "find stream info error\n"); |
19 |
av_dump_format(ic, 0, is->filename, 0); |
20 |
for(i = 0; i < ic->nb_streams; i++){ |
21 |
if(ic->streams[i])->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index == -1){ |
26 |
if(audio_index >= 0) { |
27 |
/* 所有设置SDL音频流信息的步骤都在这个函数里完成 */ |
28 |
stream_component_open(is, audio_index); |
30 |
if(is->audioStream < 0){ |
31 |
fprintf(stderr, "could not open codecs for file: %s\n", is->filename); |
34 |
/* 读包的主循环, av_read_frame不停的从文件中读取数据包(这里只取音频包)*/ |
37 |
/* 这里audioq.size是指队列中的所有数据包带的音频数据的总量,并不是包的数量 */ |
38 |
if(is->audioq.size > MAX_AUDIO_SIZE){ |
42 |
ret = av_read_frame(is->ic, packet); |
44 |
if(ret == AVERROR_EOF || url_feof(is->ic->pb)) break; |
45 |
if(is->ic->pb && is->ic->pb->error) break; |
48 |
if(packet->stream_index == is->audioStream){ |
49 |
packet_queue_put(&is->audioq, packet); |
51 |
av_free_packet(packet); |
54 |
while(!is->quit) SDL_Delay(100); |
57 |
event.type = FF_QUIT_EVENT; |
58 |
event.user.data1 = is; |
59 |
SDL_PushEvent(&event); |
stream_component_open():设置音频参数和打开设备
1 |
int stream_component_open(videoState *is, int stream_index){ |
2 |
AVFormatContext *ic = is->ic; |
3 |
AVCodecContext *codecCtx; |
5 |
/* 在用SDL_OpenAudio()打开音频设备的时候需要这两个参数*/ |
6 |
/* wanted_spec是我们期望设置的属性,spec是系统最终接受的参数 */ |
7 |
/* 我们需要检查系统接受的参数是否正确 */ |
8 |
SDL_AudioSpec wanted_spec, spec; |
9 |
int64_t wanted_channel_layout = 0; // 声道布局(SDL中的具体定义见“FFMPEG结构体”部分) |
10 |
int wanted_nb_channels; // 声道数 |
11 |
/* SDL支持的声道数为 1, 2, 4, 6 */ |
12 |
/* 后面我们会使用这个数组来纠正不支持的声道数目 */ |
13 |
const int next_nb_channels[] = { 0, 0, 1, 6, 2, 6, 4, 6 }; |
15 |
if(stream_index < 0 || stream_index >= ic->nb_streams) return -1; |
16 |
codecCtx = ic->streams[stream_index]->codec; |
17 |
wanted_nb_channels = codecCtx->channels; |
18 |
if(!wanted_channel_layout || wanted_nb_channels != av_get_channel_layout_nb_channels(wanted_channel_layout)) { |
19 |
wanted_channel_layout = av_get_default_channel_lauout(wanted_channel_nb_channels); |
20 |
wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX; |
22 |
wanted_spec.channels = av_get_channels_layout_nb_channels(wanted_channel_layout); |
23 |
wanted_spec.freq = codecCtx->sample_rate; |
24 |
if(wanted_spec.freq <= 0 || wanted_spec.channels <=0){ |
25 |
fprintf(stderr, "Invaild sample rate or channel count!\n"); |
28 |
wanted_spec.format = AUDIO_S16SYS; // 具体含义请查看“SDL宏定义”部分 |
29 |
wanted_spec.silence = 0; // 0指示静音 |
30 |
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; // 自定义SDL缓冲区大小 |
31 |
wanted_spec.callback = audio_callback; // 音频解码的关键回调函数 |
32 |
wanted_spec.userdata = is; // 传给上面回调函数的外带数据 |
34 |
/* 打开音频设备,这里使用一个while来循环尝试打开不同的声道数(由上面 */ |
35 |
/* next_nb_channels数组指定)直到成功打开,或者全部失败 */ |
36 |
while(SDL_OpenAudio(&wanted_spec, &spec) < 0){ |
37 |
fprintf(stderr, "SDL_OpenAudio(%d channels): %s\n", wanted_spec.channels, SDL_GetError()); |
38 |
wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)]; // FFMIN()由ffmpeg定义的宏,返回较小的数 |
39 |
if(!wanted_spec.channels){ |
40 |
fprintf(stderr, "No more channel to try\n"); |
43 |
wanted_channel_layout = av_get_default_channel_layout(wanted_spec.channels); |
45 |
/* 检查实际使用的配置(保存在spec,由SDL_OpenAudio()填充) */ |
46 |
if(spec.format != AUDIO_S16SYS){ |
47 |
fprintf(stderr, "SDL advised audio format %d is not supported\n", spec.format); |
50 |
if(spec.channels != wanted_spec.channels) { |
51 |
wanted_channel_layout = av_get_default_channel_layout(spec.channels); |
52 |
if(!wanted_channel_layout){ |
53 |
fprintf(stderr, "SDL advised channel count %d is not support\n", spec.channels); |
58 |
is->audio_src_fmt = is->audio_tgt_fmt = AV_SAMPLE_FMT_S16; |
59 |
is->audio_src_freq = is->audio_tgt_freq = spec.freq; |
60 |
is->audio_src_channel_layout = is->audio_tgt_layout = wanted_channel_layout; |
61 |
is->audio_src_channels = is->audio_tat_channels = spec.channels; |
63 |
codec = avcodec_find_decoder(codecCtx>codec_id); |
64 |
if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)){ |
65 |
fprintf(stderr, "Unsupported codec!\n"); |
68 |
ic->streams[stream_index]->discard = AVDISCARD_DEFAULT; //具体含义请查看“FFMPEG宏定义”部分 |
69 |
is->audioStream = stream_index; |
70 |
is->audio_st = ic->streams[stream_index]; |
71 |
is->audio_buf_size = 0; |
72 |
is->audio_buf_index = 0; |
73 |
memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); |
74 |
packet_queue_init(&is->audioq); |
75 |
SDL_PauseAudio(0); // 开始播放静音 |
audio_callback(): 回调函数,向SDL缓冲区填充数据
1 |
void audio_callback(void *userdata, Uint8 *stream, int len){ |
2 |
VideoState *is = (VideoState*)userdata; |
3 |
int len1, audio_data_size; |
5 |
/* len是由SDL传入的SDL缓冲区的大小,如果这个缓冲未满,我们就一直往里填充数据 */ |
7 |
/* audio_buf_index 和 audio_buf_size 标示我们自己用来放置解码出来的数据的缓冲区,*/ |
8 |
/* 这些数据待copy到SDL缓冲区, 当audio_buf_index >= audio_buf_size的时候意味着我*/ |
9 |
/* 们的缓冲为空,没有数据可供copy,这时候需要调用audio_decode_frame来解码出更 |
11 |
if(is->audio_buf_index >= is->audio_buf_size){ |
12 |
audio_data_size = audio_decode_frame(is); |
13 |
/* audio_data_size < 0 标示没能解码出数据,我们默认播放静音 */ |
14 |
is(audio_data_size < 0){ |
15 |
is->audio_buf_size = 1024; |
17 |
memset(is->audio_buf, 0, is->audio_buf_size); |
19 |
is->audio_buf_size = audio_data_size; |
21 |
is->audio_buf_index = 0; |
23 |
/* 查看stream可用空间,决定一次copy多少数据,剩下的下次继续copy */ |
24 |
len1 = is->audio_buf_size - is->audio_buf_index; |
25 |
if(len1 > len) len1 = len; |
27 |
memcpy(stream, (uint8_t*)is->audio_buf + is->audio_buf_index, len1); |
30 |
is->audio_buf_index += len1; |
audio_decode_frame():解码音频
1 |
int audio_decode_frame(VideoState *is){ |
2 |
int len1, len2, decoded_data_size; |
3 |
AVPacket *pkt = &is->audio_pkt; |
5 |
int64_t dec_channel_layout; |
6 |
int wanted_nb_samples, resampled_data_size; |
9 |
while(is->audio_pkt_size > 0){ |
11 |
if(!(is->audio_frame = avacodec_alloc_frame())){ |
12 |
return AVERROR(ENOMEM); |
15 |
avcodec_get_frame_defaults(is->audio_frame); |
17 |
len1 = avcodec_decode_audio4(is->audio_st_codec, is->audio_frame, got_frame, pkt); |
20 |
is->audio_pkt_size = 0; |
23 |
is->audio_pkt_data += len1; |
24 |
is->audio_pkt_size -= len1; |
25 |
if(!got_frame) continue; |
27 |
decoded_data_size = av_samples_get_buffer_size(NULL, |
28 |
is->audio_frame_channels, |
29 |
is->audio_frame_nb_samples, |
30 |
is->audio_frame_format, 1); |
31 |
dec_channel_layout = (is->audio_frame->channel_layout && is->audio_frame->channels |
32 |
== av_get_channel_layout_nb_channels(is->audio_frame->channel_layout)) |
33 |
? is->audio_frame->channel_layout : av_get_default_channel_layout(is->audio_frame->channels); |
34 |
wanted_nb_samples = is->audio_frame->nb_samples; |
35 |
if (is->audio_frame->format != is->audio_src_fmt || |
36 |
dec_channel_layout != is->audio_src_channel_layout || |
37 |
is->audio_frame->sample_rate != is->audio_src_freq || |
38 |
(wanted_nb_samples != is->audio_frame->nb_samples && !is->swr_ctx)) { |
39 |
if (is->swr_ctx) swr_free(&is->swr_ctx); |
40 |
is->swr_ctx = swr_alloc_set_opts(NULL, |
41 |
is->audio_tgt_channel_layout, |
45 |
is->audio_frame->format, |
46 |
is->audio_frame->sample_rate, |
48 |
if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) { |
49 |
fprintf(stderr, "swr_init() failed\n"); |
52 |
is->audio_src_channel_layout = dec_channel_layout; |
53 |
is->audio_src_channels = is->audio_st->codec->channels; |
54 |
is->audio_src_freq = is->audio_st->codec->sample_rate; |
55 |
is->audio_src_fmt = is->audio_st->codec->sample_fmt; |
57 |
/* 这里我们可以对采样数进行调整,增加或者减少,一般可以用来做声画同步 */ |
59 |
const uint8_t **in = (const uint8_t **)is->audio_frame->extended_data; |
60 |
uint8_t *out[] = { is->audio_buf2 }; |
61 |
if (wanted_nb_samples != is->audio_frame->nb_samples) { |
62 |
if(swr_set_compensation(is->swr_ctx, |
63 |
(wanted_nb_samples - is->audio_frame->nb_samples)*is->audio_tgt_freq/is->audio_frame->sample_rate, |
64 |
wanted_nb_samples * is->audio_tgt_freq/is->audio_frame->sample_rate) < 0) { |
65 |
fprintf(stderr, "swr_set_compensation() failed\n"); |
69 |
len2 = swr_convert(is->swr_ctx, out, |
70 |
sizeof(is->audio_buf2)/is->audio_tgt_channels/av_get_bytes_per_sample(is->audio_tgt_fmt), |
71 |
in, is->audio_frame->nb_samples); |
73 |
fprintf(stderr, "swr_convert() failed\n"); |
76 |
if(len2 == sizeof(is->audio_buf2)/is->audio_tgt_channels/av_get_bytes_per_sample(is->audio_tgt_fmt)) { |
77 |
fprintf(stderr, "warning: audio buffer is probably too small\n"); |
78 |
swr_init(is->swr_ctx); |
80 |
is->audio_buf = is->audio_buf2; |
81 |
resampled_data_size = len2*is->audio_tgt_channels*av_get_bytes_per_sample(is->audio_tgt_fmt); |
83 |
resampled_data_size = decoded_data_size; |
84 |
is->audio_buf = is->audio_frame->data[0]; |
87 |
return resampled_data_size; |
89 |
if (pkt->data) av_free_packet(pkt); |
90 |
memset(pkt, 0, sizeof(*pkt)); |
91 |
if (is->quit) return -1; |
92 |
if (packet_queue_get(&is->audioq, pkt, 1) < 0) return -1; |
93 |
is->audio_pkt_data = pkt->data; |
94 |
is->audio_pkt_size = pkt->size; |
FFMPEG结构体
channel_layout_map
5 |
} channel_layout_map[] = { |
6 |
{ "mono", 1, AV_CH_LAYOUT_MONO }, |
7 |
{ "stereo", 2, AV_CH_LAYOUT_STEREO }, |
8 |
{ "2.1", 3, AV_CH_LAYOUT_2POINT1 }, |
9 |
{ "3.0", 3, AV_CH_LAYOUT_SURROUND }, |
10 |
{ "3.0(back)", 3, AV_CH_LAYOUT_2_1 }, |
11 |
{ "4.0", 4, AV_CH_LAYOUT_4POINT0 }, |
12 |
{ "quad", 4, AV_CH_LAYOUT_QUAD }, |
13 |
{ "quad(side)", 4, AV_CH_LAYOUT_2_2 }, |
14 |
{ "3.1", 4, AV_CH_LAYOUT_3POINT1 }, |
15 |
{ "5.0", 5, AV_CH_LAYOUT_5POINT0_BACK }, |
16 |
{ "5.0(side)", 5, AV_CH_LAYOUT_5POINT0 }, |
17 |
{ "4.1", 5, AV_CH_LAYOUT_4POINT1 }, |
18 |
{ "5.1", 6, AV_CH_LAYOUT_5POINT1_BACK }, |
19 |
{ "5.1(side)", 6, AV_CH_LAYOUT_5POINT1 }, |
20 |
{ "6.0", 6, AV_CH_LAYOUT_6POINT0 }, |
21 |
{ "6.0(front)", 6, AV_CH_LAYOUT_6POINT0_FRONT }, |
22 |
{ "hexagonal", 6, AV_CH_LAYOUT_HEXAGONAL }, |
23 |
{ "6.1", 7, AV_CH_LAYOUT_6POINT1 }, |
24 |
{ "6.1", 7, AV_CH_LAYOUT_6POINT1_BACK }, |
25 |
{ "6.1(front)", 7, AV_CH_LAYOUT_6POINT1_FRONT }, |
26 |
{ "7.0", 7, AV_CH_LAYOUT_7POINT0 }, |
27 |
{ "7.0(front)", 7, AV_CH_LAYOUT_7POINT0_FRONT }, |
28 |
{ "7.1", 8, AV_CH_LAYOUT_7POINT1 }, |
29 |
{ "7.1(wide)", 8, AV_CH_LAYOUT_7POINT1_WIDE }, |
30 |
{ "octagonal", 8, AV_CH_LAYOUT_OCTAGONAL }, |
31 |
{ "downmix", 2, AV_CH_LAYOUT_STEREO_DOWNMIX, }, |
FFMPEG宏定义
Audio channel convenience macros
1 |
#define AV_CH_LAYOUT_MONO (AV_CH_FRONT_CENTER) |
2 |
#define AV_CH_LAYOUT_STEREO (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT) |
3 |
#define AV_CH_LAYOUT_2POINT1 (AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY) |
4 |
#define AV_CH_LAYOUT_2_1 (AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER) |
5 |
#define AV_CH_LAYOUT_SURROUND (AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER) |
6 |
#define AV_CH_LAYOUT_3POINT1 (AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY) |
7 |
#define AV_CH_LAYOUT_4POINT0 (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_CENTER) |
8 |
#define AV_CH_LAYOUT_4POINT1 (AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY) |
9 |
#define AV_CH_LAYOUT_2_2 (AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT) |
10 |
#define AV_CH_LAYOUT_QUAD (AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) |
11 |
#define AV_CH_LAYOUT_5POINT0 (AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT) |
12 |
#define AV_CH_LAYOUT_5POINT1 (AV_CH_LAYOUT_5POINT0|AV_CH_LOW_FREQUENCY) |
13 |
#define AV_CH_LAYOUT_5POINT0_BACK (AV_CH_LAYOUT_SURROUND|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) |
14 |
#define AV_CH_LAYOUT_5POINT1_BACK (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_LOW_FREQUENCY) |
15 |
#define AV_CH_LAYOUT_6POINT0 (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_CENTER) |
16 |
#define AV_CH_LAYOUT_6POINT0_FRONT (AV_CH_LAYOUT_2_2|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) |
17 |
#define AV_CH_LAYOUT_HEXAGONAL (AV_CH_LAYOUT_5POINT0_BACK|AV_CH_BACK_CENTER) |
18 |
#define AV_CH_LAYOUT_6POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_CENTER) |
19 |
#define AV_CH_LAYOUT_6POINT1_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_BACK_CENTER) |
20 |
#define AV_CH_LAYOUT_6POINT1_FRONT (AV_CH_LAYOUT_6POINT0_FRONT|AV_CH_LOW_FREQUENCY) |
21 |
#define AV_CH_LAYOUT_7POINT0 (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) |
22 |
#define AV_CH_LAYOUT_7POINT0_FRONT (AV_CH_LAYOUT_5POINT0|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) |
23 |
#define AV_CH_LAYOUT_7POINT1 (AV_CH_LAYOUT_5POINT1|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT) |
24 |
#define AV_CH_LAYOUT_7POINT1_WIDE (AV_CH_LAYOUT_5POINT1|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) |
25 |
#define AV_CH_LAYOUT_7POINT1_WIDE_BACK (AV_CH_LAYOUT_5POINT1_BACK|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER) |
26 |
#define AV_CH_LAYOUT_OCTAGONAL (AV_CH_LAYOUT_5POINT0|AV_CH_BACK_LEFT|AV_CH_BACK_CENTER|AV_CH_BACK_RIGHT) |
27 |
#define AV_CH_LAYOUT_STEREO_DOWNMIX (AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT) |
SDL宏定义
SDL_AudioSpec format
1 |
AUDIO_U8 Unsigned 8-bit samples |
2 |
AUDIO_S8 Signed 8-bit samples |
3 |
AUDIO_U16LSB Unsigned 16-bit samples, in little-endian byte order |
4 |
AUDIO_S16LSB Signed 16-bit samples, in little-endian byte order |
5 |
AUDIO_U16MSB Unsigned 16-bit samples, in big-endian byte order |
6 |
AUDIO_S16MSB Signed 16-bit samples, in big-endian byte order |
7 |
AUDIO_U16 same as AUDIO_U16LSB (for backwards compatability probably) |
8 |
AUDIO_S16 same as AUDIO_S16LSB (for backwards compatability probably) |
9 |
AUDIO_U16SYS Unsigned 16-bit samples, in system byte order |
10 |
AUDIO_S16SYS Signed 16-bit samples, in system byte order |
git clone https://github.com/lnmcc/musicPlayer.git
- SDL音频播放
gcc -o tutorial03 tutorial03.c -lavutil -lavformat -lavcodec -lz -lm \`sdl-config --cflags --libs` A ...
- Android 音频播放分析笔记
AudioTrack是Android中比较偏底层的用来播放音频的接口,它主要被用来播放PCM音频数据,和MediaPlayer不同,它不涉及到文件解析和解码等复杂的流程,比较适合通过它来分析Andro ...
- Qt + FFmpeg 本地音频播放器
http://pan.baidu.com/s/1hqoYXrI
- 最简单的基于FFMPEG+SDL的音频播放器 ver2 (采用SDL2.0)
===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...
- 最简单的基于FFMPEG+SDL的音频播放器 ver2 (採用SDL2.0)
===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...
- ffmpeg+SDl+ 播放器 -01
最近因公司项目需要,打算自己在LINUX平台整一个播放器,来学习和研究音频编解码. 项目需求: 支持下列格式文件播放. 1> WMA 硬件解码,但需要软件分析ASF格式,提取Payload数据 ...
- FFmpeg简易播放器的实现-音频播放
本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10068490.html 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...
- 视频播放器控制原理:ffmpeg之ffplay播放器源代码分析
版权声明:本文由张坤原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/535574001486630869 来源:腾云阁 ht ...
- ffmpeg音频播放代码示例-avcodec_decode_audio4
一.概述 最近在学习ffmpeg解码的内容,参考了官方的教程http://dranger.com/ffmpeg/tutorial03.html,结果发现这个音频解码的教程有点问题.参考了各种博客,并同 ...
随机推荐
- SpringMVC数据绑定全面示例(复杂对象,数组等)
点击链接查询原文 http://www.xdemo.org/springmvc-data-bind/ 已经使用SpringMVC开发了几个项目,平时也有不少朋友问我数据怎么传输,怎么绑定之类的话题,今 ...
- fedora下缺少autopoint包的解决办法
编译过程中,报错,缺少autopoint包 然而无论是yum install autopoint 还是yum search autopoint都没有理想的答案 执行yum install gettex ...
- 2014多校第一场 I 题 || HDU 4869 Turn the pokers(费马小定理+快速幂模)
题目链接 题意 : m张牌,可以翻n次,每次翻xi张牌,问最后能得到多少种形态. 思路 :0定义为反面,1定义为正面,(一开始都是反), 对于每次翻牌操作,我们定义两个边界lb,rb,代表每次中1最少 ...
- hdu 3595 GG and MM 博弈论
同时进行,必须操作这就是Every-SG的特点 同样在贾志豪的论文中有提到这种游戏:组合游戏略述——浅谈SG游戏的若干拓展及变形 其中这个游戏特点不仅有必胜和必败,而且有时间长短的博弈,对于自己必胜的 ...
- 日期工具类TimeUnit
import java.util.concurrent.TimeUnit; 2 3 public class TimeUnitDemo { 4 private TimeUnit timeUnit =T ...
- Linux资源监控命令/工具(网络)
1.手动/自动设定与启动/关闭IP参数:ifconfig,ifup,ifdown 这三个指令的用途都是在启动网络接口,不过,ifup与ifdown仅能就/etc/sysconfig/netw ...
- 与Google轻轻地擦肩而过
第一集 因为那几年三天两头往硅谷里飞,所以我实在记不清这个故事到底是发生在98年还是99年夏天某日的一个下午. 那天我和Excite.com的创始人Mark V. H.在Palo Alto的一家餐厅共 ...
- java:synchronized
synchronized:利用上锁实现数据同步,避免多线程操作的情况下,数据出现异常. 另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块. 一个线程获得了一个对象的同步锁,那这个对象上所 ...
- Qt学习记录--Qt::CaseSensitive
Qt::CaseSensitivity 为枚举类型, 可取值Qt::CaseSensitive 和 Qt::CaseInsensitive, 表示匹配的灵敏度. 比较字符串的时候 Qt::CaseSe ...
- VS2010/MFC编程入门教程之目录和总结
鸡啄米的这套VS2010/MFC编程入门教程到此就全部完成了,虽然有些内容还未涉及到,但帮助大家进行VS2010/MFC的入门学习业已足够.以此教程的知识为基础,学习VS2010/MFC较为深入的内容 ...