/*=========================================================================\
* Copyright(C)2016 Chudai.
*
* File name : audio_player_v1.1.c
* Version : v1.0.0
* Author : 初代
* Date : 2016/10/03
* Description :
* Function list: 1.
* 2.
* 3.
* History :
\*=========================================================================*/
/*-----------------------------------------------------------*
* 头文件 *
*-----------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#define __STDC_CONSTANT_MACROS //ffmpeg要求
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <SDL2/SDL.h>
#ifdef __cplusplus
}
#endif
//
#include "wrap_base.h"
#include "packet_queue.h"
#define AVCODE_MAX_AUDIO_FRAME_SIZE 192000 //1 second of 48khz 32bit audio
#define SDL_AUDIO_BUFFER_SIZE 1024 //
#define FILE_NAME "/home/isshe/Music/WavinFlag.aac"
#define ERR_STREAM stderr
#define OUT_SAMPLE_RATE 44100
/*
static Uint8 *audio_chunk;
static Uint32 audio_len;
static Uint8 *audio_pos;
*/
AVFrame wanted_frame;
PacketQueue audio_queue;
int quit = 0;
void audio_callback(void *userdata, Uint8 *stream, int len);
int audio_decode_frame(AVCodecContext *pcodec_ctx, uint8_t *audio_buf, int buf_size);
/*
void packet_queue_init(PacketQueue *queue);
int packet_queue_put(PacketQueue *queue, AVPacket *packet);
int packet_queue_get(PacketQueue *queue, AVPacket *packet, int block);
*/
int main(int argc, char *argv[])
{
AVFormatContext *pformat_ctx = NULL;
int audio_stream = -1;
AVCodecContext *pcodec_ctx = NULL;
AVCodecContext *pcodec_ctx_cp = NULL;
AVCodec *pcodec = NULL;
AVPacket packet ; //!
AVFrame *pframe = NULL;
char filename[256] = FILE_NAME;
//SDL
SDL_AudioSpec wanted_spec;
SDL_AudioSpec spec ;
//ffmpeg 初始化
av_register_all();
//SDL初始化
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
{
fprintf(ERR_STREAM, "Couldn't init SDL:%s\n", SDL_GetError());
exit(-1);
}
get_file_name(filename, argc, argv);
//打开文件
if (avformat_open_input(&pformat_ctx, filename, NULL, NULL) != 0)
{
fprintf(ERR_STREAM, "Couldn't open input file\n");
exit(-1);
}
//检测文件流信息
//旧版的是av_find_stream_info()
if (avformat_find_stream_info(pformat_ctx, NULL) < 0)
{
fprintf(ERR_STREAM, "Not Found Stream Info\n");
exit(-1);
}
//显示文件信息,十分好用的一个函数
av_dump_format(pformat_ctx, 0, filename, false);
//找视音频流
if (find_stream_index(pformat_ctx, NULL, &audio_stream) == -1)
{
fprintf(ERR_STREAM, "Couldn't find stream index\n");
exit(-1);
}
printf("audio_stream = %d\n", audio_stream);
//找到对应的解码器
pcodec_ctx = pformat_ctx->streams[audio_stream]->codec;
pcodec = avcodec_find_decoder(pcodec_ctx->codec_id);
if (!pcodec)
{
fprintf(ERR_STREAM, "Couldn't find decoder\n");
exit(-1);
}
/*
pcodec_ctx_cp = avcodec_alloc_context3(pcodec);
if (avcodec_copy_context(pcodec_ctx_cp, pcodec_ctx) != 0)
{
fprintf(ERR_STREAM, "Couldn't copy codec context\n");
exit(-1);
}
*/
//设置音频信息, 用来打开音频设备。
wanted_spec.freq = pcodec_ctx->sample_rate;
wanted_spec.format = AUDIO_S16SYS;
wanted_spec.channels = pcodec_ctx->channels; //通道数
wanted_spec.silence = 0; //设置静音值
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; //读取第一帧后调整?
wanted_spec.callback = audio_callback;
wanted_spec.userdata = pcodec_ctx;
//wanted_spec:想要打开的
//spec: 实际打开的,可以不用这个,函数中直接用NULL,下面用到spec的用wanted_spec代替。
//这里会开一个线程,调用callback。
//SDL_OpenAudio->open_audio_device(开线程)->SDL_RunAudio->fill(指向callback函数)
//可以用SDL_OpenAudioDevice()代替
if (SDL_OpenAudio(&wanted_spec, &spec) < 0)
{
fprintf(ERR_STREAM, "Couldn't open Audio:%s\n", SDL_GetError());
exit(-1);
}
//设置参数,供解码时候用, swr_alloc_set_opts的in部分参数
wanted_frame.format = AV_SAMPLE_FMT_S16;
wanted_frame.sample_rate = spec.freq;
wanted_frame.channel_layout = av_get_default_channel_layout(spec.channels);
wanted_frame.channels = spec.channels;
//初始化AVCondecContext,以及进行一些处理工作。
avcodec_open2(pcodec_ctx, pcodec, NULL);
//初始化队列
packet_queue_init(&audio_queue);
//可以用SDL_PauseAudioDevice()代替,目前用的这个应该是旧的。
//0是不暂停,非零是暂停
//如果没有这个就放不出声音
SDL_PauseAudio(0); //为什么要这个?
//读一帧数据
while(av_read_frame(pformat_ctx, &packet) >= 0) //读一个packet,数据放在packet.data
{
if (packet.stream_index == audio_stream)
{
packet_queue_put(&audio_queue, &packet);
}
else
{
av_free_packet(&packet);
}
}
getchar(); //...
return 0;
}
//注意userdata是前面的AVCodecContext.
//len的值常为2048,表示一次发送多少。
//audio_buf_size:一直为样本缓冲区的大小,wanted_spec.samples.(一般每次解码这么多,文件不同,这个值不同)
//audio_buf_index: 标记发送到哪里了。
//这个函数的工作模式是:
//1. 解码数据放到audio_buf, 大小放audio_buf_size。(audio_buf_size = audio_size;这句设置)
//2. 调用一次callback只能发送len个字节,而每次取回的解码数据可能比len大,一次发不完。
//3. 发不完的时候,会len == 0,不继续循环,退出函数,继续调用callback,进行下一次发送。
//4. 由于上次没发完,这次不取数据,发上次的剩余的,audio_buf_size标记发送到哪里了。
//5. 注意,callback每次一定要发且仅发len个数据,否则不会退出。
//如果没发够,缓冲区又没有了,就再取。发够了,就退出,留给下一个发,以此循环。
//三个变量设置为static就是为了保存上次数据,也可以用全局变量,但是感觉这样更好。
void audio_callback(void *userdata, Uint8 *stream, int len)
{
AVCodecContext *pcodec_ctx = (AVCodecContext *)userdata;
int len1 = 0;
int audio_size = 0;
//注意是static
//为什么要分那么大?
static uint8_t audio_buf[(AVCODE_MAX_AUDIO_FRAME_SIZE * 3) / 2];
static unsigned int audio_buf_size = 0;
static unsigned int audio_buf_index = 0;
//初始化stream,每次都要。
SDL_memset(stream, 0, len);
while(len > 0)
{
if (audio_buf_index >= audio_buf_size)
{
//数据全部发送,再去获取
//自定义的一个函数
audio_size = audio_decode_frame(pcodec_ctx, 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;
//
没有合适的资源?快使用搜索试试~ 我知道了~
ffmpeg+SDL2实现的音频播放器V2.0(无杂音)
共10个文件
c:3个
h:2个
png:1个
4星 · 超过85%的资源 需积分: 33 196 下载量 180 浏览量
2016-10-05
01:42:32
上传
评论 2
收藏 11.44MB ZIP 举报
温馨提示
ffmpeg+SDL2实现的音频播放器 博客:http://blog.csdn.net/i_scream_/article/details/52736200 中的代码。
资源推荐
资源详情
资源评论
收起资源包目录
audio_player_v1.2.zip (10个子文件)
v1.2
audio_player_v1.2.c 14KB
WavinFlag.aac 3.37MB
wrap_base.h 3KB
audio_player_v1.2 90KB
packet_queue.h 4KB
packet_queue.c 4KB
2_caichaji.mp3 8.23MB
流程图.png 54KB
Makefile 552B
wrap_base.c 2KB
共 10 条
- 1
i丶scream
- 粉丝: 61
- 资源: 18
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
- 1
- 2
- 3
前往页