转自:http://blog.sina.com.cn/s/blog_62a8419a01016exv.html
FFMPEG SDK 开发介绍
FFmpeg浅尝辄止(四)——音频的解码和编码
http://blog.csdn.net/yang_xian521/article/details/7699620
音频和视频其实是一样的,在文件中寻找音频流,然后解压出来,得到音频帧的数据,同样也可以按照设定的编码格式进行压缩,我这里把音频的解码和编码做成了两个工程,也是直接上代码:
[cpp] view plain copy print ?
- #include <stdio.h>
- #include <stdlib.h>
-
- extern "C"
- {
- #include <libavcodec\avcodec.h>
- #include <libavformat\avformat.h>
- #include <libswscale\swscale.h>
- }
-
- int main(char arg,char *argv[])
- {
- char *filename = argv[1];
-
- av_register_all();
- AVFormatContext *pInFmtCtx=NULL;
- AVCodecContext *pInCodecCtx=NULL;
- if (av_open_input_file(&pInFmtCtx, filename, NULL, 0, NULL)!=0)
- printf("av_open_input_file error\n");
- if (av_find_stream_info(pInFmtCtx) < 0)
- printf("av_find_stream_info error\n");
-
- unsigned int j;
-
-
- int audioStream = -1;
- for (j=0; j<pInFmtCtx->nb_streams; j++)
- {
- if (pInFmtCtx->streams[j]->codec->codec_type == CODEC_TYPE_AUDIO)
- {
- audioStream = j;
- break;
- }
- }
- if (audioStream == -1)
- {
- printf("input file has no audio stream\n");
- return 0;
- }
- printf("audio stream num: %d\n",audioStream);
- pInCodecCtx = pInFmtCtx->streams[audioStream]->codec;
- AVCodec *pInCodec = NULL;
-
- pInCodec = avcodec_find_decoder(pInCodecCtx->codec_id);
- if (pInCodec == NULL)
- {
- printf("error no Codec found\n");
- return -1 ;
- }
-
- if(avcodec_open(pInCodecCtx, pInCodec)<0)
- {
- printf("error avcodec_open failed.\n");
- return -1;
-
- }
-
- static AVPacket packet;
-
- printf(" bit_rate = %d \r\n", pInCodecCtx->bit_rate);
- printf(" sample_rate = %d \r\n", pInCodecCtx->sample_rate);
- printf(" channels = %d \r\n", pInCodecCtx->channels);
- printf(" code_name = %s \r\n", pInCodecCtx->codec->name);
- printf(" block_align = %d\n",pInCodecCtx->block_align);
-
- uint8_t *pktdata;
- int pktsize;
- int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*100;
- uint8_t * inbuf = (uint8_t *)malloc(out_size);
- FILE* pcm;
- pcm = fopen("result.pcm","wb");
- long start = clock();
- while (av_read_frame(pInFmtCtx, &packet) >= 0)
- {
- if(packet.stream_index==audioStream)
- {
- pktdata = packet.data;
- pktsize = packet.size;
- while(pktsize>0)
- {
- out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*100;
-
- int len = avcodec_decode_audio2(pInCodecCtx, (short*)inbuf, &out_size, pktdata, pktsize);
- if (len < 0)
- {
- printf("Error while decoding.\n");
- break;
- }
- if(out_size > 0)
- {
- fwrite(inbuf,1,out_size,pcm);
- fflush(pcm);
- }
- pktsize -= len;
- pktdata += len;
- }
- }
- av_free_packet(&packet);
- }
- long end = clock();
- printf("cost time :%f\n",double(end-start)/(double)CLOCKS_PER_SEC);
- free(inbuf);
- fclose(pcm);
- if (pInCodecCtx!=NULL)
- {
- avcodec_close(pInCodecCtx);
- }
- av_close_input_file(pInFmtCtx);
-
- return 0;
- }
#include <stdio.h>
#include <stdlib.h>
extern "C"
{
#include <libavcodec\avcodec.h>
#include <libavformat\avformat.h>
#include <libswscale\swscale.h>
}
int main(char arg,char *argv[])
{
char *filename = argv[1];
av_register_all(); //注册所有可解码类型
AVFormatContext *pInFmtCtx=NULL; //文件格式
AVCodecContext *pInCodecCtx=NULL; //编码格式
if (av_open_input_file(&pInFmtCtx, filename, NULL, 0, NULL)!=0) //获取文件格式
printf("av_open_input_file error\n");
if (av_find_stream_info(pInFmtCtx) < 0) //获取文件内音视频流的信息
printf("av_find_stream_info error\n");
unsigned int j;
// Find the first audio stream
int audioStream = -1;
for (j=0; j<pInFmtCtx->nb_streams; j++) //找到音频对应的stream
{
if (pInFmtCtx->streams[j]->codec->codec_type == CODEC_TYPE_AUDIO)
{
audioStream = j;
break;
}
}
if (audioStream == -1)
{
printf("input file has no audio stream\n");
return 0; // Didn't find a audio stream
}
printf("audio stream num: %d\n",audioStream);
pInCodecCtx = pInFmtCtx->streams[audioStream]->codec; //音频的编码上下文
AVCodec *pInCodec = NULL;
pInCodec = avcodec_find_decoder(pInCodecCtx->codec_id); //根据编码ID找到用于解码的结构体
if (pInCodec == NULL)
{
printf("error no Codec found\n");
return -1 ; // Codec not found
}
if(avcodec_open(pInCodecCtx, pInCodec)<0)//将两者结合以便在下面的解码函数中调用pInCodec中的对应解码函数
{
printf("error avcodec_open failed.\n");
return -1; // Could not open codec
}
static AVPacket packet;
printf(" bit_rate = %d \r\n", pInCodecCtx->bit_rate);
printf(" sample_rate = %d \r\n", pInCodecCtx->sample_rate);
printf(" channels = %d \r\n", pInCodecCtx->channels);
printf(" code_name = %s \r\n", pInCodecCtx->codec->name);
printf(" block_align = %d\n",pInCodecCtx->block_align);
uint8_t *pktdata;
int pktsize;
int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*100;
uint8_t * inbuf = (uint8_t *)malloc(out_size);
FILE* pcm;
pcm = fopen("result.pcm","wb");
long start = clock();
while (av_read_frame(pInFmtCtx, &packet) >= 0)//pInFmtCtx中调用对应格式的packet获取函数
{
if(packet.stream_index==audioStream)//如果是音频
{
pktdata = packet.data;
pktsize = packet.size;
while(pktsize>0)
{
out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*100;
//解码
int len = avcodec_decode_audio2(pInCodecCtx, (short*)inbuf, &out_size, pktdata, pktsize);
if (len < 0)
{
printf("Error while decoding.\n");
break;
}
if(out_size > 0)
{
fwrite(inbuf,1,out_size,pcm);//pcm记录
fflush(pcm);
}
pktsize -= len;
pktdata += len;
}
}
av_free_packet(&packet);
}
long end = clock();
printf("cost time :%f\n",double(end-start)/(double)CLOCKS_PER_SEC);
free(inbuf);
fclose(pcm);
if (pInCodecCtx!=NULL)
{
avcodec_close(pInCodecCtx);
}
av_close_input_file(pInFmtCtx);
return 0;
}
解码后的文件保存为result.pcm中,现在用这个文件压缩出新的音频文件,代码如下:
[cpp] view plain copy print ?
- void main()
- {
- int16_t *samples;
- uint8_t *audio_outbuf;
- int audio_outbuf_size;
- int audio_input_frame_size;
- double audio_pts;
-
- const char* filename = "test.wav";
- FILE *fin = fopen("result.pcm", "rb");
- AVOutputFormat *fmt;
- AVFormatContext *oc;
- AVStream * audio_st;
- av_register_all();
- fmt = guess_format(NULL, filename, NULL);
- oc = av_alloc_format_context();
- oc->oformat = fmt;
- snprintf(oc->filename, sizeof(oc->filename), "%s", filename);
- audio_st = NULL;
-
- if (fmt->audio_codec != CODEC_ID_NONE)
- {
- AVCodecContext *c;
- audio_st = av_new_stream(oc, 1);
- c = audio_st->codec;
- c->codec_id = fmt->audio_codec;
- c->codec_type = CODEC_TYPE_AUDIO;
- c->bit_rate = 128000;
- c->sample_rate = 44100;
- c->channels = 2;
- }
- if (av_set_parameters(oc, NULL) < 0)
- {
- return;
- }
- dump_format(oc, 0, filename, 1);
- if (audio_st)
- {
- AVCodecContext* c;
- AVCodec* codec;
- c = audio_st->codec;
- codec = avcodec_find_encoder(c->codec_id);
- avcodec_open(c, codec);
- audio_outbuf_size = 10000;
- audio_outbuf = (uint8_t*)av_malloc(audio_outbuf_size);
- if (c->frame_size <= 1)
- {
- audio_input_frame_size = audio_outbuf_size / c->channels;
- switch (audio_st->codec->codec_id)
- {
- case CODEC_ID_PCM_S16LE:
- case CODEC_ID_PCM_S16BE:
- case CODEC_ID_PCM_U16LE:
- case CODEC_ID_PCM_U16BE:
- audio_input_frame_size >>= 1;
- break;
- default:
- break;
- }
- }
- else
- {
- audio_input_frame_size = c->frame_size;
- }
- samples = (int16_t*)av_malloc(audio_input_frame_size*2*c->channels);
- }
- if (!fmt->flags & AVFMT_NOFILE)
- {
- if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0)
- {
- return;
- }
- }
- av_write_header(oc);
- for (;;)
- {
- if (audio_st)
- {
- audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
- }
- else
- {
- audio_pts = 0.0;
- }
- if (!audio_st || audio_pts >= 360.0)
- {
- break;
- }
- if (fread(samples, 1, audio_input_frame_size*2*audio_st->codec->channels, fin) <= 0)
- {
- break;
- }
- AVCodecContext* c;
- AVPacket pkt;
- av_init_packet(&pkt);
- c = audio_st->codec;
- pkt.size = avcodec_encode_audio(c, audio_outbuf, audio_outbuf_size, samples);
- pkt.pts = av_rescale_q(c->coded_frame->pts, c->time_base, audio_st->time_base);
- pkt.flags |= PKT_FLAG_KEY;
- pkt.stream_index = audio_st->index;
- pkt.data = audio_outbuf;
- if (av_write_frame(oc, &pkt) != 0)
- {
- return;
- }
- }
- if (audio_st)
- {
- avcodec_close(audio_st->codec);
- av_free(samples);
- av_free(audio_outbuf);
- }
- av_write_trailer(oc);
- for (int i=0; i<oc->nb_streams; i++)
- {
- av_freep(&oc->streams[i]->codec);
- av_freep(&oc->streams[i]);
- }
- if (!(fmt->flags & AVFMT_NOFILE))
- {
- url_fclose(oc->pb);
- }
- av_free(oc);
- fclose(fin);
- }
void main()
{
int16_t *samples;
uint8_t *audio_outbuf;
int audio_outbuf_size;
int audio_input_frame_size;
double audio_pts;
const char* filename = "test.wav";
FILE *fin = fopen("result.pcm", "rb"); //音频源文件
AVOutputFormat *fmt;
AVFormatContext *oc;
AVStream * audio_st;
av_register_all();
fmt = guess_format(NULL, filename, NULL);
oc = av_alloc_format_context();
oc->oformat = fmt;
snprintf(oc->filename, sizeof(oc->filename), "%s", filename);
audio_st = NULL;
if (fmt->audio_codec != CODEC_ID_NONE)
{
AVCodecContext *c;
audio_st = av_new_stream(oc, 1);
c = audio_st->codec;
c->codec_id = fmt->audio_codec;
c->codec_type = CODEC_TYPE_AUDIO;
c->bit_rate = 128000;
c->sample_rate = 44100;
c->channels = 2;
}
if (av_set_parameters(oc, NULL) < 0)
{
return;
}
dump_format(oc, 0, filename, 1);
if (audio_st)
{
AVCodecContext* c;
AVCodec* codec;
c = audio_st->codec;
codec = avcodec_find_encoder(c->codec_id);
avcodec_open(c, codec);
audio_outbuf_size = 10000;
audio_outbuf = (uint8_t*)av_malloc(audio_outbuf_size);
if (c->frame_size <= 1)
{
audio_input_frame_size = audio_outbuf_size / c->channels;
switch (audio_st->codec->codec_id)
{
case CODEC_ID_PCM_S16LE:
case CODEC_ID_PCM_S16BE:
case CODEC_ID_PCM_U16LE:
case CODEC_ID_PCM_U16BE:
audio_input_frame_size >>= 1;
break;
default:
break;
}
}
else
{
audio_input_frame_size = c->frame_size;
}
samples = (int16_t*)av_malloc(audio_input_frame_size*2*c->channels);
}
if (!fmt->flags & AVFMT_NOFILE)
{
if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0)
{
return;
}
}
av_write_header(oc);
for (;;)
{
if (audio_st)
{
audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
}
else
{
audio_pts = 0.0;
}
if (!audio_st || audio_pts >= 360.0)
{
break;
}
if (fread(samples, 1, audio_input_frame_size*2*audio_st->codec->channels, fin) <= 0)
{
break;
}
AVCodecContext* c;
AVPacket pkt;
av_init_packet(&pkt);
c = audio_st->codec;
pkt.size = avcodec_encode_audio(c, audio_outbuf, audio_outbuf_size, samples);
pkt.pts = av_rescale_q(c->coded_frame->pts, c->time_base, audio_st->time_base);
pkt.flags |= PKT_FLAG_KEY;
pkt.stream_index = audio_st->index;
pkt.data = audio_outbuf;
if (av_write_frame(oc, &pkt) != 0)
{
return;
}
}
if (audio_st)
{
avcodec_close(audio_st->codec);
av_free(samples);
av_free(audio_outbuf);
}
av_write_trailer(oc);
for (int i=0; i<oc->nb_streams; i++)
{
av_freep(&oc->streams[i]->codec);
av_freep(&oc->streams[i]);
}
if (!(fmt->flags & AVFMT_NOFILE))
{
url_fclose(oc->pb);
}
av_free(oc);
fclose(fin);
}
对应的下载链接:
音频解码:http://download.csdn.net/detail/yang_xian521/4399219
音频编码:http://download.csdn.net/detail/yang_xian521/4399293
至此,我已经实现了视频的解码编码,音频的解码编码,相信有心人肯定可以在此基础上实现视频音频的同步压缩,不过要再看一些网上的资料,也是很轻松的。至于视频的显示播放,大多数是要结合SDL实现,由于我只是浅尝辄止,只实现了图片的显示和简单的视频播放,比起我之前推荐的几个链接是弱爆了的,就不写下去了,这个系列就到这里吧,希望能给像我一样的一些入门级选手点帮助。也欢迎大神来多多指教。
用C/C++开发基于VLC SDK的视频播放器
http://blog.sina.com.cn/s/blog_62a8419a01014eey.html
在windows系统如果开发万能播放器,一般都是基本DirectShow来开发,开发也很简单,但缺点也很多,一个文件格式是否能够播放完全取决于你是否安装了正确的解析器和解码器,即使现在有了万能解器安装包也会出现很多问题,不过自从很多开源软件的出现,比如大名鼎鼎的ffmpeg,vlc等,一切都变得简单起来,基于ffmpeg sdk开发就不多介绍了,本文主要介绍基于 VLC SDK来开发自己的播放器,一切都是那么的简单.
1.先下载VLC player, http://www.videolan.org/vlc/, 安装的时候要安装开发库(SDK)
2.用VC 6.0/2003/2005/2008/2010 IDE均可开发自己的测试程序
3.创建一个控制台程序,当然创建一个窗口程序更好,由于只是为了关注于如何使用VLC SDK,所以都无所谓的
4.在工程中设置好vlc的 include和lib路径,一般VLC sdk路径是C:\Program Files\VideoLAN\VLC\sdk
5.复制libvlc.dll, libvlccore.dll,
plugins目录到工程debug目录,如果是release需要复制到release目录,目录结构如图:
之后 就是完整的代码:
#include <stdio.h>
#include <tchar.h>
#include <time.h>
#include <windows.h>
#include <vlc/vlc.h>
#pragma comment(lib, "libvlc.lib")
#pragma comment(lib, "libvlccore.lib")
int main(int argc, char* argv[])
{
libvlc_instance_t * vlc_ins = NULL;
libvlc_media_player_t * vlc_player = NULL;
libvlc_media_t * vlc_media = NULL;
const char * vlc_args[] =
{
"-I",
"dummy",
"--ignore-config",
"--extraintf=logger",
"--verbose=2",
};
// 创建一个VLC实例
vlc_ins = libvlc_new(sizeof(vlc_args)/sizeof(vlc_args[0]), vlc_args);
if(vlc_ins != NULL)
{
// 创建一个VLC播放器
vlc_player = libvlc_media_player_new(vlc_ins);
if(vlc_player != NULL)
{
// 通过文件路径创建一个媒体实例,这里是我的测试文件
vlc_media = libvlc_media_new_path(vlc_ins, "d:\\clips\\org.mp4");
if(vlc_media != NULL)
{
// 解析媒体实例
libvlc_media_parse(vlc_media);
// 获取媒体文件的播放长度, 返回 ms
libvlc_time_t duration = libvlc_media_get_duration(vlc_media);
// 此处是获取媒体包含多个的视频和音频轨以及其他类型的轨道信息
libvlc_media_track_info_t *media_tracks = NULL;
int trackCount = libvlc_media_get_tracks_info(vlc_media, &media_tracks);
// 这里是释放内存,但我测试的时候会有问题,还没仔细研究是为何
// free(media_tracks); // crash?
// 把打开的媒体文件设置给播放器
libvlc_media_player_set_media(vlc_player, vlc_media);
// 因为是windows系统,所以需要设置一个HWND给播放器作为窗口,这里就直接使用桌面窗口,这里仅是测试
libvlc_media_player_set_hwnd(vlc_player, ::GetDesktopWindow());
// 开始播放视频
libvlc_media_player_play(vlc_player);
// 这里仅是为了打印一些信息出来,20秒后退出
time_t last_time = time(NULL);
while((time(NULL) < (last_time + 20)))
{
Sleep(10);
// 获取当前播放位置
libvlc_time_t play_time = libvlc_media_player_get_time(vlc_player);
printf("playing time : %lld ms\r", (__int64)(play_time));
// 获取媒体的状态
// libvlc_state_t media_state = libvlc_media_get_state(vlc_media);
// printf("\nmedia state : %d\n", (int)(media_state));
}
// 停止
libvlc_media_player_stop(vlc_player);
// 释放
libvlc_media_release(vlc_media);
}
// 释放
libvlc_media_player_release(vlc_player);
}
// 释放
libvlc_release(vlc_ins);
}
return 0;
}
编译运行,就可以在桌面上看到播放的视频了,用VLC SDK做自己的播放器是如此的简单。