- 浏览: 235877 次
- 性别:
- 来自: 南京
最新评论
-
baby8117628:
vc下mp3 IDv1和IDV2的读取 -
gezexu:
你好,我按照你的步骤一步步进行但是安装libvorbis的时候 ...
linux如何搭建强大的FFMPEG环境 -
ini_always:
帅哥,转载也把格式做好点,另外出处也要注明一下吧。。。
MP3文件格式解析
ffmpeg提取音频播放器总结;
一:简介
从编写音频播放器代码到完成播放器编写,测试,整整5天的时间,这时间还不算之前对 ffmpeg熟悉的时间,可以说是历经千辛万苦,终于搞出来了,虽然最终效果还不是很理想,但是已经可以很流畅的播放某些歌曲了,说是某些歌曲,是因为还有些歌曲播放效果不是很好,有些许杂音,至于那些歌曲能够顺利播放,那些不能够,我现在也摸不准是什么原因导致的,有待进一步钻研,等啥时候调好了,就用自己的这个播放器听歌曲了,嘿嘿;
a:插播:)
/**************/
这一部分属于插播内容,就不用看了;
tv视频播放;
采用img_convert时,是转换成24RGB快呢,还是32RGB快呢?可能前者快吧;似乎用qimage的话只能转换成32RGB了;因为它只有三种颜色深度1-p, 8-p, 32-p所以,只能选择32-p了;
下面是AVFrame的结构,具体可以看这里:
http://cekirdek.pardus.org.tr/~ismail/ffmpeg-docs/avcodec_8h-source.html#l00424
就是一个宏定义,咳。。。
/**************/
二:音频播放器原理
音频播放器过程如下所示:
打开文件--分析文件格式--打开对应解码器--读取一音频帧--解码音频帧--音频数据写入音频设备--循环读取音频帧--再解码。。。如此循环下去;
整个播放器实现原理详细说明为,采用ffmpeg提供的API函数先用av_open_input_file打开音频文件,分析文件得到格式信息,然后识别格式,并查找对应的解码器,再得到针对此音频文件的解码器之后,用av_read_frame从音频文件中读取一帧,然后对其用 avcodec_decode_audio函数进行解码,在将解码之后的PCM音频数据直接写到audio设备(/dev/dsp)上,根据linux音频设备的原理,此时你就应该听到悦耳的歌声了;
三:重点要点说明
在这个过程当中有几处需要特别注意,下面详细说明一下:
1、不同音频文件格式对音频压缩率不同,导致对于同一个音频包,你解压出来的音频数据大小也是不一样的,这一点无需惊奇,但是对于这些解压出来的音频数据,一定要保证全部写到声卡当中去,这样才能够作为你能听到悦耳歌声的基础,这里留意一下,这只是一个基础,要想完全实现好此播放器,下一点更是不可或缺的;我之前之所以在调试时总是听到声音很杂乱,或者带有金属声,就是因为声音没有全部写到音频设备中去,当然,可能或多或少也有一些写音频数据的太快的原故;
2、在确认了解码后的数据是完整的之后,可以将数据写入到音频设备当中了(/dev/dsp),这里很关键的一点就是要对音频设备进行设置,否则你也听不到你想听到的声音:(
对音频设备的设置主要是四个方面,这不代表其他方面不设置哦:
设置采样率(有关音频采样率,在我blog前面的文章当中有说明,一般有44100hz,48000hz,22050?不记得了,你查看我blog中前面的文章吧,嘿嘿):
ioctl (fd, SNDCTL_DSP_SPEED, &(pCodecCtx->sample_rate));
设置音频声道数(这个很好理解,一般都是立体声了)
// set channels;
i = pCodecCtx->channels;
#ifdef AUDIO_DEBUG
printf ("pCodecCtx->channels:%d\n", pCodecCtx->channels);
#endif
if ((ioctl (fd, SNDCTL_DSP_CHANNELS, &i)) == -1)
{
fprintf (stderr, "Set Audio Channels %d failed:%s\n", i,
strerror (errno));
return (-1);
}
这里需要说明的一点是,如果是立体声,则此处i应该等于2,而不是1,网上很多文章这里都说明的不正确,我之前就一直以为立体声为1,单声道为0,总是不出声音,后来一狠心,改为2,尽然ok,faint;
结论:网上的东西啊,不可全信之。。。。。。。。。。。。。。。。。。。。。。。。。。。
设置量化位数(这个量化位数是指对声音的振幅进行采样的位数,以前一般是8位,现在以16位居多,更高位数对于普通用户用不着,只能在专业音乐中才有价值)
i = AFMT_S16_LE; (16位,小端存储,也即intel的倒序数据存储)
ioctl (fd, SNDCTL_DSP_SETFMT, &i);
设置音频驱动级缓存
i = (0x0004 << 16) + 0x000b; // four 2kb buffer;你看着对应改就行了(这里是四个2kb缓存)
ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &i);
这里也可以不设置,用系统默认自定义也可;
另外有一个疑问也顺带解决了:
Q:播放音频和pts有关系么?需要他来调整播放的快慢么?就像视频那样?
A:基本没有关系,至少我目前没有用到这个咚咚,pts应该实在视频当中采用到的,pts是显示时间戳,dts是解码时间戳;以后搞到视频再详细说明啦;不需要,对于写音频数据,系统,或者更准确的说驱动会自动调整,写得快,他会阻塞你的写,写的慢?你的机器该换了,嘿嘿,玩笑一个。。。增加缓存可以解决慢的问题;
Q:如何调试音频播放器?
这里需要注意两点,一点是你要保证解码后的数据确实是PCM数据;第二点是你要确定数据准确无误,全部写入音频文件,否则会出现各种各样的暴音啊之类的事情,不可预测;
有关这两点你可以分别调试;第一点,可以将解码后的数据写入一个文件当中,然后利用一些音频分析软件(能够分析PCM数据),播放即可,看你解码的数据是否正确,完整,如果没有问题,那这一步就完成了,我在这里没有卡壳,直接过;下一步,我是扔进去很多时间,由于我的指针使用不当,导致总是漏写数据,我在下面也会把我的错误代码贴出来,以做对比,大家也都可以来看看,这一点我想经常和指针打交道的就肯定没问题了的;
这里向大家推荐windows下的cooledit软件,不用找注册码,反正能试用,没问题,功能绝对够用,而且分析声音频播非常形象,郑重推荐;虽然windows和linux切换麻烦了点,嘿嘿:)不过如果你有两台电脑,另说啦。。。
下面将这个音频播放器的源代码贴出来,以便大家互相学习;
我的编译环境是
os:Neoshine linux (2.6.14-1.1644_dt_5);
硬件:普通pc机;
/***************************************************************************
* main.cc
*
* Thu Nov 9 20:47:33 2006
* Copyright 2006
* Email lsosa.BIT
* Author lsosa.BIT
****************************************************************************/
#include <avcodec.h>
#include <avformat.h>
#include <avutil.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <sys/soundcard.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sched.h>
#define ALL_DEBUG
#ifdef ALL_DEBUG
#define AV_DEBUG
#define AUDIO_DEBUG
#endif
//------------------------------------------------------------------------------
// manipulations for file
int open_file (char *file_name, int mode)
{
// open file file_name and return the file descriptor;
int fd;
if ((fd = open (file_name, mode)) < 0)
{
fprintf (stderr, " Can't open %s!\n", file_name);
exit (-1);
}
return fd;
}
int set_audio (int fd, AVCodecContext * pCodecCtx)
{
// set the properties of audio device with pCodecCtx;
int i, err;
/* 设置适当的参数,使得声音设备工作正常 */
/* 详细情况请参考Linux关于声卡编程的文档 */
i = 0;
ioctl (fd, SNDCTL_DSP_RESET, &i);
i = 0;
ioctl (fd, SNDCTL_DSP_SYNC, &i);
i = 1;
ioctl (fd, SNDCTL_DSP_NONBLOCK, &i);
// set sample rate;
#ifdef AUDIO_DEBUG
printf ("pCodecCtx->sample_rate:%d\n", pCodecCtx->sample_rate);
#endif
i = pCodecCtx->sample_rate;
if (ioctl (fd, SNDCTL_DSP_SPEED, &i) == -1)
{
fprintf (stderr, "Set speed to %d failed:%s\n", i,
strerror (errno));
return (-1);
}
if (i != pCodecCtx->sample_rate)
{
fprintf (stderr, "do not support speed %d,supported is %d\n",
pCodecCtx->sample_rate, i);
return (-1);
}
// set channels;
i = pCodecCtx->channels;
#ifdef AUDIO_DEBUG
printf ("pCodecCtx->channels:%d\n", pCodecCtx->channels);
#endif
if ((ioctl (fd, SNDCTL_DSP_CHANNELS, &i)) == -1)
{
fprintf (stderr, "Set Audio Channels %d failed:%s\n", i,
strerror (errno));
return (-1);
}
if (i != pCodecCtx->channels)
{
fprintf (stderr, "do not support channel %d,supported %d\n",
pCodecCtx->channels, i);
return (-1);
}
// set bit format;
i = AFMT_S16_LE;
if (ioctl (fd, SNDCTL_DSP_SETFMT, &i) == -1)
{
fprintf (stderr, "Set fmt to bit %d failed:%s\n", i,
strerror (errno));
return (-1);
}
if (i != AFMT_S16_LE)
{
fprintf (stderr, "do not support bit %d, supported %d\n",
AFMT_S16_LE, i);
return (-1);
}
// set application buffer size;
// i = (0x00032 << 16) + 0x000c; // 32 4kb buffer;
// ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &i);
i = 1;
ioctl (fd, SNDCTL_DSP_PROFILE, &i);
return 0;
}
void close_file (int fd)
{
// close the file pointed by file descriptor fd;
close (fd);
}
//------------------------------------------------------------------------------
// handle audio;
void display_AVCodecContext(AVCodecContext *pCodecCtx){
//
#define STDOUT stderr
fprintf(STDOUT, "pCodecCtx->bit_rate:%d\n", pCodecCtx->bit_rate);
fprintf(STDOUT, "pCodecCtx->sample_rate:%d\n", pCodecCtx->sample_rate);
fprintf(STDOUT, "pCodecCtx->channels:%d\n", pCodecCtx->channels);
fprintf(STDOUT, "pCodecCtx->frame_size:%d\n", pCodecCtx->frame_size);
fprintf(STDOUT, "pCodecCtx->frame_number:%d\n", pCodecCtx->frame_number);
fprintf(STDOUT, "pCodecCtx->delay:%d\n", pCodecCtx->delay);
fprintf(STDOUT, "pCodecCtx->frame_bits:%d\n", pCodecCtx->frame_bits);
}
// error if return -1;
// success if return 0;
// 这里要用到指向指针的指针,否则传不到值;
int av_init (char *file_name, AVFormatContext ** pFormatCtx,
AVCodecContext ** pCodecCtx, int *p_audioStream)
{
// init the codec and format of input file file_name;
int audioStream, i;
AVCodec *pCodec;
// catch error
assert(file_name != NULL);
assert(*pFormatCtx != NULL);
assert(*pCodecCtx != NULL);
// Register all formats and codecs
av_register_all ();
// open file
if (av_open_input_file (pFormatCtx, file_name, NULL, 0, NULL) != 0){
// Couldn't open file
fprintf (stderr, " Can't open %s!\n", file_name);
return -1;
}
// Retrieve stream information
if (av_find_stream_info (*pFormatCtx) < 0){
// Couldn't find stream information
return -1;
}
#ifdef AV_DEBUG
// Dump information about file onto standard error
dump_format (*pFormatCtx, 0, file_name, false);
#endif
// Find the first audio and video stream respectively
audioStream = -1;
for (i = 0; i < (*pFormatCtx)->nb_streams; i++){
if ((*pFormatCtx)->streams[i]->codec->codec_type ==
CODEC_TYPE_AUDIO)
{
audioStream = i;
}
}
#ifdef AV_DEBUG
// dump_stream_info(pFormatCtx);
#endif
// exclude error
if (audioStream == -1){
// Didn't find a audio or video stream
return -1;
}
// Get a pointer to the codec context for the audio stream
*pCodecCtx = (*pFormatCtx)->streams[audioStream]->codec;
// Find the decoder for the audio stream
pCodec = avcodec_find_decoder ((*pCodecCtx)->codec_id);
if (pCodec == NULL)
return -1; // Codec not found
// Open codec
if (avcodec_open ((*pCodecCtx), pCodec) < 0){
return -1; // Could not open codec
}
#ifdef AUDIO_DEBUG
// printf ("pCodecCtx->sample_rate:%d, audioStream:%d\n", (*pCodecCtx)->sample_rate, audioStream);
// display_AVCodecContext(*pCodecCtx);
#endif
*p_audioStream = audioStream;
return 0;
}
void av_play (AVFormatContext * pFormatCtx,
AVCodecContext * pCodecCtx, int audioStream)
{
// which was read from one frame;
AVPacket packet;
uint32_t len;
uint8_t decompressed_audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
int decompressed_audio_buf_size;
uint8_t * p_decompressed_audio_buf;
int fd = -1; // audio file or test file?
char filename[64] = "/dev/dsp";
int mode = O_WRONLY;
//
// open audio file or written file
// printf("fd:%d", fd);
fd = open_file(filename, mode);
// printf("fd:%d", fd);
//
set_audio(fd, pCodecCtx);
//
printf("(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2=%d\n", (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2);
printf("AVCODEC_MAX_AUDIO_FRAME_SIZE=%d\n", AVCODEC_MAX_AUDIO_FRAME_SIZE);
// for a test
// char test_file[256] = "my_pcm.pcm";
// fd = open_file(test_file, mode);
#ifdef AV_DEBUG
static int size = 0;
#endif
//
// set the sched priority
// 这是为了提高音频优先级;不晓得起作用没;
int policy = SCHED_FIFO;
sched_setscheduler(0, policy, NULL);
int write_buf_size = 4196;
int written_size;
while (av_read_frame (pFormatCtx, &packet) >= 0)
{
// Is this a packet from the audio stream?
// 判断是否音频帧;
if (packet.stream_index == audioStream)
{
// Decode audio frame
// 解码音频数据为pcm数据;
len = avcodec_decode_audio (pCodecCtx,
(int16_t *)decompressed_audio_buf,
&decompressed_audio_buf_size, // it is the decompressed frame in BYTES 解码后的数据大小,字节为单位;
packet.data,
packet.size );
// printf("len:%d, packet.size:%d\n", len, packet.size);
if ( len < 0 ){
// if error len = -1
printf("+----- error in decoding audio frame\n");
// exit(0);
}
// test lsosa
// printf("size = %d\n", size);
//******************************************************************
// 重点是这一部分,使用oss播放的代码,之前的数据写是否完整的问题就是出在这里,或者是前面的set_audio函数设置不正确;
// audio_buf_info info;
p_decompressed_audio_buf = decompressed_audio_buf;
while ( decompressed_audio_buf_size > 0 ){
// 解码后数据不为零,则播放之,为零,则;
written_size = write(fd, p_decompressed_audio_buf, decompressed_audio_buf_size);
if ( written_size == -1 ){
// printf("error:decompressed_audio_buf_size:%d, decompressed_audio_buf_size:%d, %s\n", \
decompressed_audio_buf_size, decompressed_audio_buf_size,strerror(errno));
// usleep(100);
continue;
}
// printf("decompressed_audio_buf_size:%d, written_size:%d\n", \
decompressed_audio_buf_size, written_size);
decompressed_audio_buf_size -= written_size;
p_decompressed_audio_buf += written_size;
}// end while
//******************************************************************
}
else
{
printf("+----- this is not audio frame\n");
}// end if
// Free the packet that was allocated by av_read_frame
av_free_packet (&packet);
}// end while of reading one frame;
close_file(fd);
}
void av_close (AVFormatContext * pFormatCtx, AVCodecContext * pCodecCtx)
{
// close the file and codec
// Close the codec
avcodec_close (pCodecCtx);
// Close the video file
av_close_input_file (pFormatCtx);
}
//------------------------------------------------------------------------------
int main (int argc, char **argv){
//
AVFormatContext *pFormatCtx;
int audioStream = -1;
AVCodecContext *pCodecCtx;
// exclude the error about args;
if ( argc != 2 ){
printf("please give a file name\n");
exit(0);
}
// 注意:这里要用到指向指针的指针,是因为这个初始化函数需要对指针的地址进行改动,
// 所以,只有这么做,才能达到目的;
if ( av_init(argv[1], &pFormatCtx, &pCodecCtx, &audioStream) < 0 ){
//
fprintf(stderr, "error when av_init\n");
}
// play the audio file
av_play(pFormatCtx, pCodecCtx, audioStream);
// close all the opend files
av_close(pFormatCtx, pCodecCtx);
}
一:简介
从编写音频播放器代码到完成播放器编写,测试,整整5天的时间,这时间还不算之前对 ffmpeg熟悉的时间,可以说是历经千辛万苦,终于搞出来了,虽然最终效果还不是很理想,但是已经可以很流畅的播放某些歌曲了,说是某些歌曲,是因为还有些歌曲播放效果不是很好,有些许杂音,至于那些歌曲能够顺利播放,那些不能够,我现在也摸不准是什么原因导致的,有待进一步钻研,等啥时候调好了,就用自己的这个播放器听歌曲了,嘿嘿;
a:插播:)
/**************/
这一部分属于插播内容,就不用看了;
tv视频播放;
采用img_convert时,是转换成24RGB快呢,还是32RGB快呢?可能前者快吧;似乎用qimage的话只能转换成32RGB了;因为它只有三种颜色深度1-p, 8-p, 32-p所以,只能选择32-p了;
下面是AVFrame的结构,具体可以看这里:
http://cekirdek.pardus.org.tr/~ismail/ffmpeg-docs/avcodec_8h-source.html#l00424
就是一个宏定义,咳。。。
/**************/
二:音频播放器原理
音频播放器过程如下所示:
打开文件--分析文件格式--打开对应解码器--读取一音频帧--解码音频帧--音频数据写入音频设备--循环读取音频帧--再解码。。。如此循环下去;
整个播放器实现原理详细说明为,采用ffmpeg提供的API函数先用av_open_input_file打开音频文件,分析文件得到格式信息,然后识别格式,并查找对应的解码器,再得到针对此音频文件的解码器之后,用av_read_frame从音频文件中读取一帧,然后对其用 avcodec_decode_audio函数进行解码,在将解码之后的PCM音频数据直接写到audio设备(/dev/dsp)上,根据linux音频设备的原理,此时你就应该听到悦耳的歌声了;
三:重点要点说明
在这个过程当中有几处需要特别注意,下面详细说明一下:
1、不同音频文件格式对音频压缩率不同,导致对于同一个音频包,你解压出来的音频数据大小也是不一样的,这一点无需惊奇,但是对于这些解压出来的音频数据,一定要保证全部写到声卡当中去,这样才能够作为你能听到悦耳歌声的基础,这里留意一下,这只是一个基础,要想完全实现好此播放器,下一点更是不可或缺的;我之前之所以在调试时总是听到声音很杂乱,或者带有金属声,就是因为声音没有全部写到音频设备中去,当然,可能或多或少也有一些写音频数据的太快的原故;
2、在确认了解码后的数据是完整的之后,可以将数据写入到音频设备当中了(/dev/dsp),这里很关键的一点就是要对音频设备进行设置,否则你也听不到你想听到的声音:(
对音频设备的设置主要是四个方面,这不代表其他方面不设置哦:
设置采样率(有关音频采样率,在我blog前面的文章当中有说明,一般有44100hz,48000hz,22050?不记得了,你查看我blog中前面的文章吧,嘿嘿):
ioctl (fd, SNDCTL_DSP_SPEED, &(pCodecCtx->sample_rate));
设置音频声道数(这个很好理解,一般都是立体声了)
// set channels;
i = pCodecCtx->channels;
#ifdef AUDIO_DEBUG
printf ("pCodecCtx->channels:%d\n", pCodecCtx->channels);
#endif
if ((ioctl (fd, SNDCTL_DSP_CHANNELS, &i)) == -1)
{
fprintf (stderr, "Set Audio Channels %d failed:%s\n", i,
strerror (errno));
return (-1);
}
这里需要说明的一点是,如果是立体声,则此处i应该等于2,而不是1,网上很多文章这里都说明的不正确,我之前就一直以为立体声为1,单声道为0,总是不出声音,后来一狠心,改为2,尽然ok,faint;
结论:网上的东西啊,不可全信之。。。。。。。。。。。。。。。。。。。。。。。。。。。
设置量化位数(这个量化位数是指对声音的振幅进行采样的位数,以前一般是8位,现在以16位居多,更高位数对于普通用户用不着,只能在专业音乐中才有价值)
i = AFMT_S16_LE; (16位,小端存储,也即intel的倒序数据存储)
ioctl (fd, SNDCTL_DSP_SETFMT, &i);
设置音频驱动级缓存
i = (0x0004 << 16) + 0x000b; // four 2kb buffer;你看着对应改就行了(这里是四个2kb缓存)
ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &i);
这里也可以不设置,用系统默认自定义也可;
另外有一个疑问也顺带解决了:
Q:播放音频和pts有关系么?需要他来调整播放的快慢么?就像视频那样?
A:基本没有关系,至少我目前没有用到这个咚咚,pts应该实在视频当中采用到的,pts是显示时间戳,dts是解码时间戳;以后搞到视频再详细说明啦;不需要,对于写音频数据,系统,或者更准确的说驱动会自动调整,写得快,他会阻塞你的写,写的慢?你的机器该换了,嘿嘿,玩笑一个。。。增加缓存可以解决慢的问题;
Q:如何调试音频播放器?
这里需要注意两点,一点是你要保证解码后的数据确实是PCM数据;第二点是你要确定数据准确无误,全部写入音频文件,否则会出现各种各样的暴音啊之类的事情,不可预测;
有关这两点你可以分别调试;第一点,可以将解码后的数据写入一个文件当中,然后利用一些音频分析软件(能够分析PCM数据),播放即可,看你解码的数据是否正确,完整,如果没有问题,那这一步就完成了,我在这里没有卡壳,直接过;下一步,我是扔进去很多时间,由于我的指针使用不当,导致总是漏写数据,我在下面也会把我的错误代码贴出来,以做对比,大家也都可以来看看,这一点我想经常和指针打交道的就肯定没问题了的;
这里向大家推荐windows下的cooledit软件,不用找注册码,反正能试用,没问题,功能绝对够用,而且分析声音频播非常形象,郑重推荐;虽然windows和linux切换麻烦了点,嘿嘿:)不过如果你有两台电脑,另说啦。。。
下面将这个音频播放器的源代码贴出来,以便大家互相学习;
我的编译环境是
os:Neoshine linux (2.6.14-1.1644_dt_5);
硬件:普通pc机;
/***************************************************************************
* main.cc
*
* Thu Nov 9 20:47:33 2006
* Copyright 2006
* Email lsosa.BIT
* Author lsosa.BIT
****************************************************************************/
#include <avcodec.h>
#include <avformat.h>
#include <avutil.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <sys/soundcard.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sched.h>
#define ALL_DEBUG
#ifdef ALL_DEBUG
#define AV_DEBUG
#define AUDIO_DEBUG
#endif
//------------------------------------------------------------------------------
// manipulations for file
int open_file (char *file_name, int mode)
{
// open file file_name and return the file descriptor;
int fd;
if ((fd = open (file_name, mode)) < 0)
{
fprintf (stderr, " Can't open %s!\n", file_name);
exit (-1);
}
return fd;
}
int set_audio (int fd, AVCodecContext * pCodecCtx)
{
// set the properties of audio device with pCodecCtx;
int i, err;
/* 设置适当的参数,使得声音设备工作正常 */
/* 详细情况请参考Linux关于声卡编程的文档 */
i = 0;
ioctl (fd, SNDCTL_DSP_RESET, &i);
i = 0;
ioctl (fd, SNDCTL_DSP_SYNC, &i);
i = 1;
ioctl (fd, SNDCTL_DSP_NONBLOCK, &i);
// set sample rate;
#ifdef AUDIO_DEBUG
printf ("pCodecCtx->sample_rate:%d\n", pCodecCtx->sample_rate);
#endif
i = pCodecCtx->sample_rate;
if (ioctl (fd, SNDCTL_DSP_SPEED, &i) == -1)
{
fprintf (stderr, "Set speed to %d failed:%s\n", i,
strerror (errno));
return (-1);
}
if (i != pCodecCtx->sample_rate)
{
fprintf (stderr, "do not support speed %d,supported is %d\n",
pCodecCtx->sample_rate, i);
return (-1);
}
// set channels;
i = pCodecCtx->channels;
#ifdef AUDIO_DEBUG
printf ("pCodecCtx->channels:%d\n", pCodecCtx->channels);
#endif
if ((ioctl (fd, SNDCTL_DSP_CHANNELS, &i)) == -1)
{
fprintf (stderr, "Set Audio Channels %d failed:%s\n", i,
strerror (errno));
return (-1);
}
if (i != pCodecCtx->channels)
{
fprintf (stderr, "do not support channel %d,supported %d\n",
pCodecCtx->channels, i);
return (-1);
}
// set bit format;
i = AFMT_S16_LE;
if (ioctl (fd, SNDCTL_DSP_SETFMT, &i) == -1)
{
fprintf (stderr, "Set fmt to bit %d failed:%s\n", i,
strerror (errno));
return (-1);
}
if (i != AFMT_S16_LE)
{
fprintf (stderr, "do not support bit %d, supported %d\n",
AFMT_S16_LE, i);
return (-1);
}
// set application buffer size;
// i = (0x00032 << 16) + 0x000c; // 32 4kb buffer;
// ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &i);
i = 1;
ioctl (fd, SNDCTL_DSP_PROFILE, &i);
return 0;
}
void close_file (int fd)
{
// close the file pointed by file descriptor fd;
close (fd);
}
//------------------------------------------------------------------------------
// handle audio;
void display_AVCodecContext(AVCodecContext *pCodecCtx){
//
#define STDOUT stderr
fprintf(STDOUT, "pCodecCtx->bit_rate:%d\n", pCodecCtx->bit_rate);
fprintf(STDOUT, "pCodecCtx->sample_rate:%d\n", pCodecCtx->sample_rate);
fprintf(STDOUT, "pCodecCtx->channels:%d\n", pCodecCtx->channels);
fprintf(STDOUT, "pCodecCtx->frame_size:%d\n", pCodecCtx->frame_size);
fprintf(STDOUT, "pCodecCtx->frame_number:%d\n", pCodecCtx->frame_number);
fprintf(STDOUT, "pCodecCtx->delay:%d\n", pCodecCtx->delay);
fprintf(STDOUT, "pCodecCtx->frame_bits:%d\n", pCodecCtx->frame_bits);
}
// error if return -1;
// success if return 0;
// 这里要用到指向指针的指针,否则传不到值;
int av_init (char *file_name, AVFormatContext ** pFormatCtx,
AVCodecContext ** pCodecCtx, int *p_audioStream)
{
// init the codec and format of input file file_name;
int audioStream, i;
AVCodec *pCodec;
// catch error
assert(file_name != NULL);
assert(*pFormatCtx != NULL);
assert(*pCodecCtx != NULL);
// Register all formats and codecs
av_register_all ();
// open file
if (av_open_input_file (pFormatCtx, file_name, NULL, 0, NULL) != 0){
// Couldn't open file
fprintf (stderr, " Can't open %s!\n", file_name);
return -1;
}
// Retrieve stream information
if (av_find_stream_info (*pFormatCtx) < 0){
// Couldn't find stream information
return -1;
}
#ifdef AV_DEBUG
// Dump information about file onto standard error
dump_format (*pFormatCtx, 0, file_name, false);
#endif
// Find the first audio and video stream respectively
audioStream = -1;
for (i = 0; i < (*pFormatCtx)->nb_streams; i++){
if ((*pFormatCtx)->streams[i]->codec->codec_type ==
CODEC_TYPE_AUDIO)
{
audioStream = i;
}
}
#ifdef AV_DEBUG
// dump_stream_info(pFormatCtx);
#endif
// exclude error
if (audioStream == -1){
// Didn't find a audio or video stream
return -1;
}
// Get a pointer to the codec context for the audio stream
*pCodecCtx = (*pFormatCtx)->streams[audioStream]->codec;
// Find the decoder for the audio stream
pCodec = avcodec_find_decoder ((*pCodecCtx)->codec_id);
if (pCodec == NULL)
return -1; // Codec not found
// Open codec
if (avcodec_open ((*pCodecCtx), pCodec) < 0){
return -1; // Could not open codec
}
#ifdef AUDIO_DEBUG
// printf ("pCodecCtx->sample_rate:%d, audioStream:%d\n", (*pCodecCtx)->sample_rate, audioStream);
// display_AVCodecContext(*pCodecCtx);
#endif
*p_audioStream = audioStream;
return 0;
}
void av_play (AVFormatContext * pFormatCtx,
AVCodecContext * pCodecCtx, int audioStream)
{
// which was read from one frame;
AVPacket packet;
uint32_t len;
uint8_t decompressed_audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
int decompressed_audio_buf_size;
uint8_t * p_decompressed_audio_buf;
int fd = -1; // audio file or test file?
char filename[64] = "/dev/dsp";
int mode = O_WRONLY;
//
// open audio file or written file
// printf("fd:%d", fd);
fd = open_file(filename, mode);
// printf("fd:%d", fd);
//
set_audio(fd, pCodecCtx);
//
printf("(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2=%d\n", (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2);
printf("AVCODEC_MAX_AUDIO_FRAME_SIZE=%d\n", AVCODEC_MAX_AUDIO_FRAME_SIZE);
// for a test
// char test_file[256] = "my_pcm.pcm";
// fd = open_file(test_file, mode);
#ifdef AV_DEBUG
static int size = 0;
#endif
//
// set the sched priority
// 这是为了提高音频优先级;不晓得起作用没;
int policy = SCHED_FIFO;
sched_setscheduler(0, policy, NULL);
int write_buf_size = 4196;
int written_size;
while (av_read_frame (pFormatCtx, &packet) >= 0)
{
// Is this a packet from the audio stream?
// 判断是否音频帧;
if (packet.stream_index == audioStream)
{
// Decode audio frame
// 解码音频数据为pcm数据;
len = avcodec_decode_audio (pCodecCtx,
(int16_t *)decompressed_audio_buf,
&decompressed_audio_buf_size, // it is the decompressed frame in BYTES 解码后的数据大小,字节为单位;
packet.data,
packet.size );
// printf("len:%d, packet.size:%d\n", len, packet.size);
if ( len < 0 ){
// if error len = -1
printf("+----- error in decoding audio frame\n");
// exit(0);
}
// test lsosa
// printf("size = %d\n", size);
//******************************************************************
// 重点是这一部分,使用oss播放的代码,之前的数据写是否完整的问题就是出在这里,或者是前面的set_audio函数设置不正确;
// audio_buf_info info;
p_decompressed_audio_buf = decompressed_audio_buf;
while ( decompressed_audio_buf_size > 0 ){
// 解码后数据不为零,则播放之,为零,则;
written_size = write(fd, p_decompressed_audio_buf, decompressed_audio_buf_size);
if ( written_size == -1 ){
// printf("error:decompressed_audio_buf_size:%d, decompressed_audio_buf_size:%d, %s\n", \
decompressed_audio_buf_size, decompressed_audio_buf_size,strerror(errno));
// usleep(100);
continue;
}
// printf("decompressed_audio_buf_size:%d, written_size:%d\n", \
decompressed_audio_buf_size, written_size);
decompressed_audio_buf_size -= written_size;
p_decompressed_audio_buf += written_size;
}// end while
//******************************************************************
}
else
{
printf("+----- this is not audio frame\n");
}// end if
// Free the packet that was allocated by av_read_frame
av_free_packet (&packet);
}// end while of reading one frame;
close_file(fd);
}
void av_close (AVFormatContext * pFormatCtx, AVCodecContext * pCodecCtx)
{
// close the file and codec
// Close the codec
avcodec_close (pCodecCtx);
// Close the video file
av_close_input_file (pFormatCtx);
}
//------------------------------------------------------------------------------
int main (int argc, char **argv){
//
AVFormatContext *pFormatCtx;
int audioStream = -1;
AVCodecContext *pCodecCtx;
// exclude the error about args;
if ( argc != 2 ){
printf("please give a file name\n");
exit(0);
}
// 注意:这里要用到指向指针的指针,是因为这个初始化函数需要对指针的地址进行改动,
// 所以,只有这么做,才能达到目的;
if ( av_init(argv[1], &pFormatCtx, &pCodecCtx, &audioStream) < 0 ){
//
fprintf(stderr, "error when av_init\n");
}
// play the audio file
av_play(pFormatCtx, pCodecCtx, audioStream);
// close all the opend files
av_close(pFormatCtx, pCodecCtx);
}
发表评论
-
vc下mp3 IDv1和IDV2的读取
2010-01-25 10:52 2425/*这是修改后的代码,VC下读ID3v2 & ID3v ... -
使用ffmpeg为库编写的小型多媒体播放器源代码
2010-01-21 16:52 4375今天突发奇想,就在以前音频播放器(详细情况请看这里——http ... -
ffmpeg开发指南
2010-01-20 17:26 3397ffmpeg 中的Libavformat 和 li ... -
linux下安装ffmpeg过程
2010-01-18 15:48 1905最近互联网视频共享的 ... -
【PNG overview】PNG专题!
2010-01-18 13:39 3399【PNG overview】PNG专题! 作者 鼯鼠 ... -
Big Endian 和 Little Endian
2010-01-18 13:29 1566Peter Lee 2008-04-20 一、字节序 ... -
MediaInfo开源工程
2010-01-18 13:22 2398一、简介 MediaInfo 用来 ... -
MP3文件格式解析
2010-01-18 10:58 3575MP3文件格式解析 Peter Lee 2008-06-0 ... -
LAME-mp3
2010-01-18 10:40 2053LAME - 压缩 MP3 的最佳利 ... -
FLV文件格式分析(图示讲解的清楚)
2010-01-14 15:56 5120FLV是一个二进制文件, ... -
我对FLV 文件格式的理解
2010-01-14 15:52 3386我对FLV 文件格式的理解 ----------------- ... -
常用的音频文件介绍
2010-01-13 10:56 1416MP3全称是动态影像专家压缩标准音频层面3(Moving Pi ... -
RTSP客户端的JAVA实现
2010-01-12 16:12 8370参考资料 1. 《RTSP简单命 ... -
国外嵌入式、音视频处理等重要网站
2010-01-08 10:07 2051嵌入式方面: 1.关于嵌入式开发的站点,提供非常多关于嵌入 ... -
RTSP点播——消息流程实例
2010-01-08 09:44 5129RTSP点播消息流程实例(客户端:VLC, RTSP服务器:L ... -
live555代码解读之三:SETUP和PLAY请求消息处理过程
2010-01-08 09:43 3480SETUP请求消息处理过程 ... -
live555代码解读之二:DESCRIBE请求消息处理过程
2010-01-08 09:42 3821ve555代码解读之二:DESCRIBE请求消息处理过程 ... -
live555代码解读之一:RTSP连接的建立过程
2010-01-08 09:42 4453TSPServer类用于构建一个RTSP服务器,该类同时在其内 ... -
live555源代码概述
2010-01-08 09:41 3899述 liveMedia项目(http://www ... -
浅议SDP(会话描述协议)
2010-01-04 15:25 3587因为最近常常使用到SDP(会话描述协议Session Desc ...
相关推荐
FFmpeg是一款强大的开源多媒体处理工具,它包含了众多用于音频和视频处理的功能,如提取音频、解码播放等。本文将详细介绍如何使用FFmpeg进行音频提取和播放解码,并结合具体的命令行示例来帮助理解其操作流程。 ...
一、实验目的 1、深入掌握视音频的基本参数信息 2、掌握ffmpeg编译环境配置 3、掌握和熟悉提取视音频文件的基本方法 ...4、对该视频文件,提取音频信息,保存为wav格式。结果利用adobe audition播放并截图。
一、实验目的 1、深入掌握视音频的基本参数信息 2、掌握ffmpeg编译环境配置 3、掌握和熟悉提取视音频文件的基本方法 ...4、对该视频文件,提取音频信息,保存为wav格式。结果利用adobe audition播放并截图。
请确保你已经安装了FFmpeg,并将`input.mp4`替换为你要提取音频的MP4文件的路径,将`output.mp3`替换为你想要保存音频的文件名和格式。 该命令行中的选项含义如下: - `-vn`:指定不编码视频。 - `-acodec libmp3...
在本场景中,我们将重点讨论如何使用FFmpeg从MP4视频中提取音频并将其转换为MP3格式。这对于音频处理、视频编辑或二次开发项目非常有用,比如C#应用或其他编程语言的应用。 首先,我们需要了解FFmpeg的基本结构和...
(解压后,文件名中的_修改为.号) vc++写的 一、实验目的 1、深入掌握视音频的基本参数信息 2、掌握ffmpeg编译环境配置 ...4、对该视频文件,提取音频信息,保存为wav格式。结果利用adobe audition播放并截图。
FFmpeg是一款强大的开源多媒体处理工具,它支持各种音频和视频格式的编码、解码、转换、流媒体处理等操作。在最新的版本中,FFmpeg提供了提取任意格式视频帧的功能,这对于视频编辑、分析或者制作静态图像非常有用。...
"avformat-54.dll"是FFmpeg的多媒体容器格式处理库,负责解析不同类型的容器格式(如MP4、AVI、MKV等),提取音频和视频流,并提供时间同步功能。 "avfilter-3.dll"是FFmpeg的滤镜库,它允许对视频流进行实时的视觉...
它支持众多的音视频编码格式和容器,能进行视频解码、编码、转码、提取音频、视频流,以及实时音视频处理等操作。 **视频播放器实现的关键技术** 1. **多媒体文件解析**:FFmpeg的libavformat库负责读取和解析...
ffmpeg提取音频&拼接音频-附件资源
文件视频数据获取是指从视频文件中提取音频数据。在FFmpeg中,可以使用其强大的解码能力读取视频文件,并分离出音频流进行播放。这在处理包含音频的多媒体文件时非常有用,例如MV或者带有背景音乐的视频。 总结来说...
在本场景中,我们要探讨的是如何利用FFmpeg从视频文件中提取音频,这一过程通常用于分离音轨以便独立编辑或保存为单独的音频文件。 首先,FFmpeg的核心功能之一就是其强大的解码能力。它能处理大量的视频和音频编码...
**基于ffmpeg的音频处理Java SDK**是用于在Java应用程序中实现高效音频操作的工具包,它利用了开源的ffmpeg库的强大功能。ffmpeg是一个强大的多媒体处理框架,能够处理各种音频和视频格式,包括编码、解码、转码、流...
2. **音频剪切**:从音频文件中提取特定部分。例如,要剪切00:00:10到00:00:30的时间段,命令可能是`ffmpeg -i input.mp3 -ss 00:00:10 -t 00:00:20 output.mp3`。`-ss`参数设置起始时间,`-t`参数定义剪切的时长。 ...
FFmpeg是一款开源的跨平台多媒体处理工具,广泛用于视频、音频的编码、解码、转换、流媒体等任务。在本项目中,FFmpeg被封装成了一个完整的播放器,适用于工程或项目中直接调用,以实现视频回放功能。这个播放器的...
在Android播放器中,SDL主要负责显示视频帧和处理用户输入,而FFmpeg则负责从视频文件中提取并解码数据。首先,FFmpeg读取视频文件,识别其格式并分离出音频和视频流。然后,libavcodec解码这些流,生成原始的音频...
在IT领域,尤其是在多媒体处理和开发中,`FFmpeg`是一个不可或缺的工具,它是一个开源的命令行工具,用于处理音频、视频以及流媒体。`FFmpeg`支持各种编码、解码、转换、封装和流化操作。在这个场景中,我们将讨论...
源码可能包含了解析音频头部信息,提取音频流,并将其转化为可播放数据的模块。音频解码是关键步骤,可能使用到开源的音频解码库如FFmpeg。解码后的数据会转换为PCM(脉冲编码调制)格式,这是大多数硬件可以直接...
使用ffmpeg.exe获取文件属性信息,ffmpeg是java开发的用于多媒体文件编辑的命令行工具,有多个版本,功能比较强大,C#中可以在进程外异步调用这个工具 using (System.Diagnostics.Process pro = new System....
在标题“ffmpeg提取视频流”中,我们关注的核心知识点是FFmpeg如何从视频文件中提取视频流并进行操作。在这个场景下,描述提到生成PPM文件,PPM是一种简单的、未压缩的位图图像格式,通常用于中间处理或调试目的。 ...