正在学习用ffmpeg写一个播放器,但是学习到tutorial3的时候播放视频没有声音,大家看看哪里出了问题啊
// Tutorial.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
extern "C"
{
#include"libavcodec\avcodec.h"
#include"libavformat\avformat.h"
#include "sdl\SDL.h"
#include "sdl\SDL_thread.h"
#include "libswscale\swscale.h"
}
#define SDL_AUDIO_BUFFER_SIZE 1024
#define MAX_AUDIO_FRAME_SIZE 192000
const char *VideoFile="e:\测试片源\1.mpeg";
int quit=0;
typedef struct PacketQueue
{
AVPacketList *first_pkt,*last_pkt;
int nb_packets;
int size;
SDL_mutex *mutex;
SDL_cond *cond;
}PacketQueue;
PacketQueue audioq;
void packet_queue_init(PacketQueue *q)
{
memset(q,0,sizeof(PacketQueue));
q->mutex=SDL_CreateMutex();
q->cond=SDL_CreateCond();
}
int packet_queue_put(PacketQueue *q,AVPacket *pkt)
{
AVPacketList *pktl;
if(av_dup_packet(pkt)<0)
{
printf("%s 存在问题",pkt);
getchar();
return -1;
}
pktl=(AVPacketList *)av_malloc(sizeof(AVPacketList));
if(!pktl)
{
return -1;
}
pktl->pkt=*pkt;
pktl->next=NULL;
SDL_LockMutex(q->mutex);
if(!q->last_pkt)
{
q->first_pkt=pktl;
}
else
{
q->last_pkt->next=pktl;
}
q->last_pkt=pktl;
q->nb_packets++;
q->size+=pktl->pkt.size;
SDL_CondSignal(q->cond);
SDL_UnlockMutex(q->mutex);
return 0;
}
static int packet_queue_get(PacketQueue *q,AVPacket *pkt,int block)
{
AVPacketList *pktl;
int ret;
SDL_LockMutex(q->mutex);
for(;;)
{
if(quit)
{
ret=-1;
break;
}
pktl=q->first_pkt;
if(pktl)
{
q->first_pkt=pktl->next;
if(!q->first_pkt)
{
q->last_pkt=NULL;
}
q->nb_packets--;
q->size-=pktl->pkt.size;
*pkt=pktl->pkt;
av_free(pktl);
ret=1;
break;
}
else
{
SDL_CondWait(q->cond,q->mutex);
}
}
SDL_UnlockMutex(q->mutex);
return ret;
}
int decode_interrupt_cb(void*)
{
return quit;
}
void SaveFrame(AVFrame *pFrame,int width,int height,int iFrame)
{
FILE *pFile;
char szFilename[32];
int y;
sprintf(szFilename,"frame %d.ppm",iFrame);
pFile=fopen(szFilename,"wb");
if(pFile==NULL)
{
printf("储存失败");
getchar();
return;
}
fprintf(pFile,"P6\n%d %d\n255\n",width,height);
for(y=0;y<height;y++)
{
fwrite(pFrame->data[0]+y*pFrame->linesize[0],1,width*3,pFile);
}
fclose(pFile);
}
int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf,int buf_size) {
static AVPacket pkt;
static uint8_t *audio_pkt_data = NULL;
static int audio_pkt_size = 0;
static AVFrame frame;
int len1, data_size = 0;
for (;;)
{
while (audio_pkt_size > 0)
{
int got_frame = 0;
len1 = avcodec_decode_audio4(aCodecCtx, &frame, &got_frame, &pkt);
if (len1 < 0)
{
/* if error, skip frame */
audio_pkt_size = 0;
break;
}
audio_pkt_data += len1;
audio_pkt_size -= len1;
if (got_frame)
{
data_size = frame.linesize[0];
/*
data_size = av_samples_get_buffer_size(NULL,
aCodecCtx->channels, frame.nb_samples,
aCodecCtx->sample_fmt, 1);
*/
memcpy(audio_buf, frame.data[0], data_size);
}
if (data_size <= 0)
{
/* No data yet, get more frames */
continue;
}
/* We have data, return it and come back for more later */
return data_size;
}
if (pkt.data)
av_free_packet(&pkt);
if (quit)
{
return -1;
}
if (packet_queue_get(&audioq, &pkt, 1) < 0)
{
return -1;
}
audio_pkt_data = pkt.data;
audio_pkt_size = pkt.size;
}
return 0;
}
void audio_callback(void *userdata,uint8_t *stream,int len)
{
AVCodecContext *aCodecCtx=(AVCodecContext*)userdata;
int len1,audio_size;
static uint8_t audio_buf[(MAX_AUDIO_FRAME_SIZE*3)/2];
static unsigned int audio_buf_size=0;
static unsigned int audio_buf_index=0;
while(len>0)
{
if(audio_buf_index>=audio_buf_size)
{
audio_size=audio_decode_frame(aCodecCtx,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;
if(len1>len)
{
len1=len;
}
memcpy(stream,(uint8_t*)audio_buf+audio_buf_size,len1);
len-=len1;
stream+=len1;
audio_buf_index+=len1;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
SDL_Event event;
av_register_all();
AVFormatContext *pFormatCtx=NULL;
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
{
fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
exit(1);
}
if(avformat_open_input(&pFormatCtx,VideoFile,NULL,NULL)!=0)
{
printf("无法打开视频文件");
getchar();
return -1;
}
if(av_find_stream_info(pFormatCtx)<0)
{
printf("没有找到流信息");
getchar();
return -1;
}
static const AVIOInterruptCB int_cb={ decode_interrupt_cb, NULL};
pFormatCtx->interrupt_callback=int_cb;
av_dump_format(pFormatCtx,0,VideoFile,0);
int i,iVideoStream,iAudioStream;
AVCodecContext *pCodecCtx=NULL;
AVCodecContext *aCodecCtx=NULL;
iVideoStream=-1;
iAudioStream=-1;
for(i=0;i<pFormatCtx->nb_streams;i++)
{
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO&&iVideoStream<0)
{
iVideoStream=i;
}
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO&&iAudioStream<0)
{
iAudioStream=i;
}
}
if(-1==iVideoStream||-1==iAudioStream)
{
printf("没有找到视频流或音频流");
getchar();
return -1;
}
pCodecCtx=pFormatCtx->streams[iVideoStream]->codec;
aCodecCtx=pFormatCtx->streams[iAudioStream]->codec;
SDL_AudioSpec wanted_spec,spec;
wanted_spec.freq=aCodecCtx->sample_rate;
wanted_spec.format=AUDIO_S16SYS;
wanted_spec.channels=aCodecCtx->channels;
wanted_spec.silence=0;
wanted_spec.samples=SDL_AUDIO_BUFFER_SIZE;
wanted_spec.callback=audio_callback;
wanted_spec.userdata=aCodecCtx;
AVCodec *pCodec=NULL;
AVCodec *aCodec=NULL;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
aCodec=avcodec_find_decoder(aCodecCtx->codec_id);
if(NULL==pCodec||NULL==aCodec)
{
printf("未能找到解码器");
getchar();
return -1;
}
if(SDL_OpenAudio(&wanted_spec,&spec)<0)
{
printf("SDL_OpenAudio:%s\n",SDL_GetError());
getchar();
return -1;
}
if(avcodec_open2(pCodecCtx,pCodec,NULL)<0||avcodec_open2(aCodecCtx,aCodec,NULL)<0)
{
printf("打开解码器失败");
getchar();
return -1;
}
AVFrame *pFrame=NULL;
AVFrame *pFrameRGB=NULL;
pFrame=avcodec_alloc_frame();
pFrameRGB=avcodec_alloc_frame();
if(NULL==pFrameRGB)
{
printf("分配储存失败");
getchar();
return -1;
}
uint8_t *buffer;
int numBytes;
numBytes=avpicture_get_size(PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height);
buffer=(uint8_t*)av_malloc(numBytes*sizeof(uint8_t));
avpicture_fill((AVPicture*)pFrameRGB,buffer,PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height);
////////////////////////
SDL_Surface *screen=NULL;
screen=SDL_SetVideoMode(pCodecCtx->width,pCodecCtx->height,0,0);
if(NULL==screen)
{
printf("SDL创建失败");
getchar();
return -1;
}
packet_queue_init(&audioq);
SDL_PauseAudio(0);
SDL_Overlay *bmp=NULL;
bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,SDL_YV12_OVERLAY, screen);
SDL_Rect rect;
struct SwsContext *img_convert_ctx;
/////////////////////////////////////
int frameFinished=0;
AVPacket packet;
i=0;
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
while(av_read_frame(pFormatCtx,&packet)>=0)
{
if(packet.stream_index==iVideoStream)
{
avcodec_decode_video2(pCodecCtx,pFrame,&frameFinished,&packet);
if(frameFinished)
{
SDL_LockYUVOverlay(bmp);
#if 0
AVPicture pict;
pict.data[0]=bmp->pixels[0];
pict.data[1]=bmp->pixels[2];
pict.data[2]=bmp->pixels[1];
pict.linesize[0]=bmp->pitches[0];
pict.linesize[1]=bmp->pitches[2];
pict.linesize[2]=bmp->pitches[1];
#else
bmp->pixels[0]=pFrameRGB->data[0];
bmp->pixels[2]=pFrameRGB->data[1];
bmp->pixels[1]=pFrameRGB->data[2];
bmp->pitches[0]=pFrameRGB->linesize[0];
bmp->pitches[2]=pFrameRGB->linesize[1];
bmp->pitches[1]=pFrameRGB->linesize[2];
#endif
//转换图片为YUV格式
sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
SDL_UnlockYUVOverlay(bmp);
rect.x = 0;
rect.y = 0;
rect.w = pCodecCtx->width;
rect.h = pCodecCtx->height;
SDL_DisplayYUVOverlay(bmp, &rect);
/*if(++i<=50)
{
SaveFrame(pFrameRGB,pCodecCtx->width,pCodecCtx->height,i);
}*/
}
}
else if(packet.stream_index==iAudioStream)
{
packet_queue_put(&audioq,&packet);
}
else
{
av_free_packet(&packet);
}
}
SDL_PollEvent(&event);
switch(event.type)
{
case SDL_QUIT:
quit=1;
}
av_free(buffer);
av_free(pFrameRGB);
av_free(pFrame);
avcodec_close(pCodecCtx);
av_close_input_file(pFormatCtx);
getchar();
return 0;
}
解决方案
40
在怀疑有问题的地方加写日志到文件。
有时不将“调用函数名字+各参数值,进入函数后各参数值,中间变量值,退出函数前准备返回的值,返回函数到调用处后函数名字+各参数值+返回值”这些信息写日志到文件中是无论怎么样也发现不了问题在哪里的,包括捕获各种异常、写日志到屏幕、单步或设断点或生成core文件、……这些方法都不行! 写日志到文件参考下面:
有时不将“调用函数名字+各参数值,进入函数后各参数值,中间变量值,退出函数前准备返回的值,返回函数到调用处后函数名字+各参数值+返回值”这些信息写日志到文件中是无论怎么样也发现不了问题在哪里的,包括捕获各种异常、写日志到屏幕、单步或设断点或生成core文件、……这些方法都不行! 写日志到文件参考下面:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <windows.h>
#include <io.h>
#else
#include <unistd.h>
#include <sys/time.h>
#include <pthread.h>
#define CRITICAL_SECTION pthread_mutex_t
#define _vsnprintf vsnprintf
#endif
//Log{
#define MAXLOGSIZE 20000000
#define MAXLINSIZE 16000
#include <time.h>
#include <sys/timeb.h>
#include <stdarg.h>
char logfilename1[]="MyLog1.log";
char logfilename2[]="MyLog2.log";
static char logstr[MAXLINSIZE+1];
char datestr[16];
char timestr[16];
char mss[4];
CRITICAL_SECTION cs_log;
FILE *flog;
#ifdef WIN32
void Lock(CRITICAL_SECTION *l) {
EnterCriticalSection(l);
}
void Unlock(CRITICAL_SECTION *l) {
LeaveCriticalSection(l);
}
#else
void Lock(CRITICAL_SECTION *l) {
pthread_mutex_lock(l);
}
void Unlock(CRITICAL_SECTION *l) {
pthread_mutex_unlock(l);
}
#endif
void LogV(const char *pszFmt,va_list argp) {
struct tm *now;
struct timeb tb;
if (NULL==pszFmt||0==pszFmt[0]) return;
_vsnprintf(logstr,MAXLINSIZE,pszFmt,argp);
ftime(&tb);
now=localtime(&tb.time);
sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
sprintf(timestr,"%02d:%02d:%02d",now->tm_hour ,now->tm_min ,now->tm_sec );
sprintf(mss,"%03d",tb.millitm);
printf("%s %s.%s %s",datestr,timestr,mss,logstr);
flog=fopen(logfilename1,"a");
if (NULL!=flog) {
fprintf(flog,"%s %s.%s %s",datestr,timestr,mss,logstr);
if (ftell(flog)>MAXLOGSIZE) {
fclose(flog);
if (rename(logfilename1,logfilename2)) {
remove(logfilename2);
rename(logfilename1,logfilename2);
}
} else {
fclose(flog);
}
}
}
void Log(const char *pszFmt,...) {
va_list argp;
Lock(&cs_log);
va_start(argp,pszFmt);
LogV(pszFmt,argp);
va_end(argp);
Unlock(&cs_log);
}
//Log}
int main(int argc,char * argv[]) {
int i;
#ifdef WIN32
InitializeCriticalSection(&cs_log);
#else
pthread_mutex_init(&cs_log,NULL);
#endif
for (i=0;i<10000;i++) {
Log("This is a Log %04d from FILE:%s LINE:%d\n",i, __FILE__, __LINE__);
}
#ifdef WIN32
DeleteCriticalSection(&cs_log);
#else
pthread_mutex_destroy(&cs_log);
#endif
return 0;
}
//1-78行添加到你带main的.c或.cpp的那个文件的最前面
//81-85行添加到你的main函数开头
//89-93行添加到你的main函数结束前
//在要写LOG的地方仿照第87行的写法写LOG到文件MyLog1.log中