MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//ffmpeg初始化
av_register_all();
avformat_network_init();
avdevice_register_all();
out_buffer = NULL;
pFrame = NULL;
pFrameYUV = NULL;
pCodecCtx = NULL;
//编码用到的
AVCodecContext* pCodecCtx1;
AVCodec* pCodec1;
pCodecCtx1 = NULL;
pCodec1 = NULL;
// AVFormatContext *pFormatCtx = avformat_alloc_context();
// AVInputFormat *ifmt=av_find_input_format("dshow");
// XiaoMi USB 2.0 Webcam
// avformat_open_input(&pFormatCtx,"video=e2eSoft VCam",ifmt,NULL) ;
AVCodec *pCodec = NULL;
打开摄像头设备
AVFormatContext *pFormatCtx = avformat_alloc_context();
AVDictionary* options = NULL;
// av_dict_set(&options,"list_devices","true",0);//显示可用的dshow设备
AVInputFormat *iformat = av_find_input_format("dshow");
if( avformat_open_input(&pFormatCtx,"video=XiaoMi USB 2.0 Webcam",iformat,NULL) != 0)//dummy
{
qDebug()<<"Couldn't open input stream video.(无法打开输入流)\n";
}
打开麦克风设备
QString audioDevName("麦克风 (Realtek High Definition Audio)");
QString audioDevOption = QString("audio=%1").arg(audioDevName);
if( avformat_open_input(&pFormatCtx,audioDevOption.toUtf8(),iformat,NULL) != 0)//dummy
{
qDebug()<<"Couldn't open input stream video.(无法打开麦克风)\n";
}
if(avformat_find_stream_info(pFormatCtx,NULL)<0)
{
qDebug()<<"Couldn't find stream information.";
return ;
}
//视频解码
videoindex =-1;
pCodecCtx = NULL;
for(i=0; i<pFormatCtx->nb_streams; i++)
{
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
{
videoindex=i;
break;
}
}
if(videoindex==-1)
{
qDebug()<<"Didn't find a video stream.(没有找到视频流)";
return ;
}
pCodecCtx = pFormatCtx->streams[videoindex]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
{
qDebug()<<"Codec not found.";
return ;
}
if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)
{
qDebug()<<"Could not open codec.";
return ;
}
pFrame=avcodec_alloc_frame();
pFrameYUV=avcodec_alloc_frame();
out_buffer=(uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
/// \brief ret
///
audioindex = -1;
aCodecCtx = NULL;
for(i=0; i<pFormatCtx->nb_streams; i++)
{
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
{
audioindex=i;
break;
}
}
if(audioindex==-1)
{
qDebug()<<"Didn't find a video stream.(没有找到音频流)";
return;
}
aCodecCtx = pFormatCtx->streams[audioindex]->codec;
aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
if(aCodec == NULL)
{
qDebug()<<"audio Codec not found.";
return;
}
if(avcodec_open2(aCodecCtx, aCodec,NULL)<0)
{
qDebug()<<"Could not open video codec.";
return;
}
aFrame=avcodec_alloc_frame();
int ret, got_frame;
struct SwsContext *img_convert_ctx = NULL;
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
//读取frame
AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));
const QString tfileName = "JJJ.yuv";
const QString tfileName2 = "JJJ.h264";
const QString tfileName3 = "jjj.pcm";
const QString tfileName4 = "jjj.aac";
QFile tmpfile,tmpfile2,tmpfile3,tmpfile4;
tmpfile.setFileName(tfileName);
tmpfile2.setFileName(tfileName2);
tmpfile3.setFileName(tfileName3);
tmpfile4.setFileName(tfileName4);
tmpfile.open(QIODevice::WriteOnly);
if (!tmpfile.isOpen())
{
qDebug() << "file is not open";return;
}
if (!tmpfile.isWritable())
{
qDebug() << "file write disable";
return;
}
tmpfile2.open(QIODevice::WriteOnly);
tmpfile3.open(QIODevice::WriteOnly);
tmpfile4.open(QIODevice::WriteOnly);
//编码相关
AVPacket pkt;
av_new_packet(&pkt,640*480*3);
AVPacket pkt1;
av_new_packet(&pkt1,4096);
int size;
int in_w = 640;
int in_h = 480;//宽高
//查找并打开h264编码器
pCodec1 = avcodec_find_encoder(AV_CODEC_ID_H264);
if(!pCodec1)
{
qDebug()<<"h264 codec not found";
}
pCodecCtx1 = avcodec_alloc_context3(pCodec1);
pCodecCtx1->codec_id = AV_CODEC_ID_H264;
pCodecCtx1->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx1->pix_fmt = PIX_FMT_YUV420P;
pCodecCtx1->width = in_w;
pCodecCtx1->height = in_h;
pCodecCtx1->time_base.num = 1;
pCodecCtx1->time_base.den = 15;//帧率(既一秒钟多少张图片)
pCodecCtx1->bit_rate = 600000; //比特率(调节这个大小可以改变编码后视频的质量)
pCodecCtx1->gop_size=12;
// some formats want stream headers to be separate
if (pCodecCtx1->flags & AVFMT_GLOBALHEADER)
pCodecCtx1->flags |= CODEC_FLAG_GLOBAL_HEADER;
// Set Option
AVDictionary *param = 0;
//H.264
//av_dict_set(¶m, "preset", "slow", 0);
av_dict_set(¶m, "preset", "superfast", 0);
av_dict_set(¶m, "tune", "zerolatency", 0); //实现实时编码
//pCodec1 = avcodec_find_encoder(pCodecCtx1->codec_id);
// if (!pCodec1){
// qDebug()<<"Can not find video encoder! 没有找到合适的编码器!";
// return ;
// }
if (avcodec_open2(pCodecCtx1, pCodec1,¶m) < 0){
qDebug()<<"Failed to open video encoder! 编码器打开失败!";
return ;
}
//查找并打开AAC编码器
AVCodecContext* pCodecCtx2; AVCodec* aCodec1;
AVFrame* frame;
int ONEFrameSize = 0; //一帧长度传入ffmpeg的
int mAacBufferIndex = 0;
int mAacBufferSize = 0;
//这段代码必须放到前面
aCodec1 = avcodec_find_encoder(AV_CODEC_ID_AAC);//pCodecCtx2->codec_id
if (!aCodec1)
{
qDebug()<<"没有找到合适的编码器!";
return;
}
#if 1
pCodecCtx2 = avcodec_alloc_context3(aCodec1);
pCodecCtx2->codec_id = AV_CODEC_ID_AAC;
pCodecCtx2->codec_type = AVMEDIA_TYPE_AUDIO;
pCodecCtx2->sample_fmt = AV_SAMPLE_FMT_S16;
pCodecCtx2->sample_rate= 44100;
pCodecCtx2->channel_layout=AV_CH_LAYOUT_STEREO;
pCodecCtx2->channels = av_get_channel_layout_nb_channels(pCodecCtx2->channel_layout);
pCodecCtx2->bit_rate = 64000;
if (avcodec_open2(pCodecCtx2, aCodec1,NULL) < 0)
{
qDebug()<<"编码器打开失败!";
return;
}
//之前没有声音是因为frame没有初始化完成
frame = avcodec_alloc_frame();//不是av_frame_alloc()
frame->nb_samples = pCodecCtx2->frame_size;
frame->format = pCodecCtx2->sample_fmt;
ONEFrameSize = av_samples_get_buffer_size(NULL,pCodecCtx2->channels,pCodecCtx2->frame_size,pCodecCtx2->sample_fmt, 1);
#endif
while(1)
{
if(av_read_frame(pFormatCtx, packet) < 0)
{
break;
}
if(packet->stream_index==videoindex)
{
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_frame, packet);
if(ret < 0)
{
qDebug()<<"video Decode Error.(解码错误)";
return;
}
if(got_frame && pCodecCtx)
{
sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
//将yuv420p的图像写入文件
int y_size = pCodecCtx->width*pCodecCtx->height;
// 640 480
// qDebug()<<y_size << pCodecCtx->width << pCodecCtx->height;
//保存成yuv格式文件 注释掉
// tmpfile.write((const char*)pFrameYUV->data[0],y_size);
// tmpfile.write((const char*)pFrameYUV->data[1],y_size/4);
// tmpfile.write((const char*)pFrameYUV->data[2],y_size/4);
//编码成h264
// picture->data[0] = node.buffer; // 亮度Y
// picture->data[1] = node.buffer + y_size; // U
// picture->data[2] = node.buffer + y_size*5/4; // V
int got_picture=0;
//编码
int ret = avcodec_encode_video2(pCodecCtx1, &pkt,pFrameYUV, &got_picture);
if (got_picture==1)
{
// bool isKeyFrame = pkt.flags & AV_PKT_FLAG_KEY; //判断是否关键帧
//int w = fwrite(pkt.data,1,pkt.size,h264Fp); //写入文件中 (h264的裸数据 直接写入文件 也可以播放 因为这里包含H264关键帧)
tmpfile2.write((const char*)pkt.data,pkt.size);
}
av_free_packet(&pkt);
}
}
else if(packet->stream_index==audioindex)
{
//解码音频帧
///这里打印出音频的信息
//qDebug()<<"audio info:"<<aCodecCtx->sample_fmt<<aCodecCtx->bit_rate<<aCodecCtx->sample_rate<<aCodecCtx->channels;
//解码声音数据成PCM数据
ret = avcodec_decode_audio4(aCodecCtx, aFrame, &got_frame, packet);
if(ret < 0)
{
return;
}
if (got_frame&&aCodecCtx)
{
int pcmSize = av_samples_get_buffer_size(NULL,aCodecCtx->channels, aFrame->nb_samples,aCodecCtx->sample_fmt, 1);
uint8_t * pcmBuffer = aFrame->data[0];
float useTime = aFrame->nb_samples * 1.0 / aCodecCtx->sample_rate;
//Time += useTime;
//qDebug()<<useTime;//<<i<<Time
//fwrite(pcmBuffer,1,pcmSize,fp_pcm);
写入文件
//tmpfile3.write((const char*)pcmBuffer,pcmSize);
//编码成AAC
//每次传递给编码器的数据大小必须要是上数的"ONEFrameSize"
//因此有了下面的while循环
#if 1
while(1)
{
int size=pcmSize-mAacBufferIndex;
if(size < ONEFrameSize)
{
memcpy(pcmBuffer,pcmBuffer+mAacBufferIndex,size);
mAacBufferIndex = 0;
pcmSize = size;
break;
}
frame->data[0] = pcmBuffer+mAacBufferIndex;
mAacBufferIndex += ONEFrameSize;
int got_frame1=0;
//编码
ret = avcodec_encode_audio2(pCodecCtx2, &pkt1,frame, &got_frame1);
if(ret < 0)
{
qDebug()<<"avcodec_encode_audio2"<<ret;
}
if (got_frame1==1)
{
/// 编码后的数据是带ADTS头的 因此写入文件后 可以直接用播放器播放
//fwrite(pkt.data,1,pkt.size,aacFp);
tmpfile4.write((const char*)pkt1.data,pkt1.size);
av_free_packet(&pkt1);
}
}
#endif
}
}
av_free_packet(packet);
}
}
用的是ffmpeg2.5 仅用于测试