基于vs2017 vc++ ffmpeg4.0.2下测试
ffmpeg 环境配置请百度(vs2017 ffmpeg )图像和声音请安装screencapturer便于查找
部分方法在https://blog.csdn.net/Java_lilin/article/details/85118365中查找
需要pthread
#include "pch.h"
#include <iostream>
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include "libavutil/imgutils.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/audio_fifo.h"
#include <libavutil/time.h>
#include <libavutil/mathematics.h>
#define HAVE_STRUCT_TIMESPEC
#include "pthread.h"
}
/*采集声音和桌面到文件或者rmtp*/
typedef struct _Packet {
int64_t pts;//时间
AVPacket pkt;//meiyizhen
struct _Packet *next;
}Packet;
typedef struct _PacketList {
struct _Packet *packet;
struct _Packet *end;
int pkgSize;
int max;
pthread_mutex_t mutex;
pthread_cond_t cond;
pthread_cond_t wait;
int exit;
}PacketList;
typedef struct GobleData{
int64_t audiotime;
}GobleData;
typedef struct MyOutPutStream {
GobleData *gobledata;
PacketList *packetList;
pthread_t pthread;
int64_t nextshowpts;
AVFormatContext *formatCtx;
AVCodecContext *ctx;//yuanlai de
AVCodecContext *enctx;//目标压缩器
AVStream *st;//
int runing;int64_t nexttps;
AVFrame *frame;//
AVFrame *outframe;//转目标
//video
SwsContext *img_convert_ctx;
//声音
AVFrame *newframe;
AVFrame *newframe2;
AVAudioFifo *fifo;
SwrContext *swr;
}MyOutPutStream;
static int initPakcetList(PacketList **ppacketList,int max) {
int ret = 0;
PacketList* p = NULL;
*ppacketList = (PacketList*)av_mallocz(sizeof(PacketList));
p = *ppacketList;
if (p == NULL)return -1;
ret = pthread_mutex_init(&p->mutex,NULL);
if (ret != 0) {
av_freep(p);
return -1;
}
ret= pthread_cond_init(&p->cond, NULL);
if (ret != 0) {
av_freep(&p);
return -1;
}
ret = pthread_cond_init(&p->wait, NULL);
if (ret != 0) {
av_freep(&p);
return -1;
}
p->max = max;
return 0;
}
static void destoryPakcetList(PacketList *packetList) {
if (packetList == NULL)return;
{
Packet *pkt=NULL;
PacketList *p = packetList;
pthread_mutex_lock(&p->mutex);
p->exit = 1;
pkt = p->packet;
while (pkt) {
Packet *temp = pkt->next;
if (temp != NULL) {
av_packet_unref(&temp->pkt);
}
pkt = temp;
}
pthread_mutex_unlock(&p->mutex);
}
}
static int pushPakcet(PacketList *p,AVPacket *pkt,int64_t pts) {
int ret = 0;
Packet *packet = NULL;
if (p == NULL || p->exit)return 0;
pthread_mutex_lock(&p->mutex);
while (!p->exit && p->pkgSize > p->max) {
pthread_cond_wait(&p->wait, &p->mutex);
}
if (p->exit) {
pthread_mutex_unlock(&p->mutex);
return 0;
}
packet = (Packet*)av_mallocz(sizeof(Packet));
if (packet) {
packet->pts = pts;
av_packet_ref(&packet->pkt,pkt);
if (p->end == NULL) {
p->packet = packet;
}
else {
p->end->next = packet;
}
p->end = packet;
p->pkgSize++;
ret = 1;
pthread_cond_signal(&p->cond);
}
pthread_mutex_unlock(&p->mutex);
return ret;
}
static int64_t popPacket(PacketList *p, AVPacket *pkt) {
int64_t ret = 0;
if (p == NULL || p->exit )return 0;
pthread_mutex_lock(&p->mutex);
while (!p->exit && p->pkgSize < 1) {
pthread_cond_wait(&p->cond,&p->mutex);
}
if (p->exit) {
pthread_mutex_unlock(&p->mutex);
return -1;
}
if (p->packet == p->end) {
ret = p->packet->pts;
av_packet_ref(pkt,&p->packet->pkt);
av_packet_unref(&p->packet->pkt);
av_freep(&p->packet);
p->packet = NULL;
p->end = NULL;
}
else {
Packet *temp = p->packet;
p->packet = temp->next;
ret = temp->pts;
av_packet_ref(pkt, &temp->pkt);
av_packet_unref(&temp->pkt);
av_freep(&temp);
}
p->pkgSize--;
pthread_cond_signal(&p->wait);
pthread_mutex_unlock(&p->mutex);
return ret;
}
static int add_video(AVFormatContext *oc, MyOutPutStream *ost) {
AVCodec *encodec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!encodec) {
printf("not found encoder for H264");
avcodec_get_name(AV_CODEC_ID_H264);
return -1;
}
ost->st = avformat_new_stream(oc, encodec);
ost->st->id = oc->nb_streams - 1;
ost->enctx = avcodec_alloc_context3(encodec);
ost->enctx->codec_id = AV_CODEC_ID_H264;
ost->enctx->bit_rate = 8000000;//码率
ost->enctx->width = 640;
ost->enctx->height = 480;
AVRational base; base.num = 1; base.den = 15;//帧率
ost->enctx->time_base = base;
ost->enctx->gop_size = 12;
ost->enctx->max_b_frames = 0;
ost->enctx->pix_fmt = AV_PIX_FMT_YUV420P;//h264必须
ost->enctx->gop_size = 12;
ost->enctx->max_b_frames = 0;
//减少延迟
av_opt_set(ost->enctx->priv_data, "preset", "superfast", 0);
av_opt_set(ost->enctx->priv_data, "tune", "zerolatency", 0);
//播放器播放不出来
if (oc->oformat->flags&AVFMT_GLOBALHEADER) {
ost->enctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
AVDictionary* opt = NULL;
int ret = avcodec_open2(ost->enctx, encodec, &opt);
av_dict_free(&opt);
if (ret < 0) {
printf("not open video codec");
return -1;
}
//add关联 把编辑器参数传递给输出流
avcodec_parameters_from_context(ost->st->codecpar, ost->enctx);
return 0;
}
static int add_audio(AVFormatContext *oc, MyOutPutStream *ost) {
AVOutputFormat *fmt = oc->oformat;
fmt->audio_codec = AV_CODEC_ID_AAC;//AV_CODEC_ID_MP3 mp3格式 改下面4个
AVCodec *encodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if (!encodec) {
printf("not found encoder for aac");
avcodec_get_name(AV_CODEC_ID_AAC);
return -1;
}
ost->st = avformat_new_stream(oc, encodec);
ost->st->id = oc->nb_streams - 1;
/*******************************/
//test4 代码
ost->enctx = avcodec_alloc_context3(encodec);
ost->enctx->codec_id = AV_CODEC_ID_AAC;
ost->enctx->sample_fmt = encodec->sample_fmts ? encodec->sample_fmts[0] : AV_SAMPLE_FMT_FLT;
ost->enctx->bit_rate = 128000;
ost->enctx->sample_rate = 44100;
if (encodec->supported_samplerates) {//查找是否支持
ost->enctx->sample_rate = encodec->supported_samplerates[0];
for (int i = 0; encodec->supported_samplerates[i]; i++)
{
if (encodec->supported_samplerates[i] == 44100) { ost->enctx->sample_rate = 44100; }
}
}
ost->enctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
ost->enctx->channel_layout = AV_CH_LAYOUT_STEREO;
if (encodec->channel_layouts) {//查找是否支持
ost->enctx->channel_layout = encodec->channel_layouts[0];
for (int i = 0; encodec->channel_layouts[i]; i++)
{
if (encodec->channel_layouts[i] == AV_CH_LAYOUT_STEREO) { ost->enctx->channel_layout = AV_CH_LAYOUT_STEREO; }
}
}
ost->enctx->channels = av_get_channel_layout_nb_channels(ost->enctx->channel_layout);
//播放器播放不出来
if (oc->oformat->flags&AVFMT_GLOBALHEADER) {
ost->enctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
AVDictionary* opt = NULL;
int ret = avcodec_open2(ost->enctx, encodec, &opt);
av_dict_free(&opt);
if (ret < 0) {
printf("not open audio codec");
return -1;
}
//add关联 把编辑器参数传递给输出流
avcodec_parameters_from_context(ost->st->codecpar, ost->enctx);
return 0;
}
static int open_video(MyOutPutStream *ost) {
//添加test1的代码 begin
ost->formatCtx = avformat_alloc_context();
AVInputFormat *ifmt = av_find_input_format("gdigrab");//设备类型
//AVInputFormat *ifmt = av_find_input_format("dshow");//设备类型
AVDictionary* options = NULL;
//av_dict_set(&options, "video_size","1920*1080",0);//大小 默认全部
av_dict_set(&options, "framerate", "15", 0);//帧lu
if (avformat_open_input(&ost->formatCtx, "desktop", ifmt, &options) != 0) {
//if (avformat_open_input(&formatCtx, "video=Integrated Camera", ifmt, &options) != 0) {
printf("open input device fail\n");
return -1;
}
av_dict_free(&options);
if (avformat_find_stream_info(ost->formatCtx, NULL) < 0) {
printf("avformat_find_stream_info faill\n");
return -1;
}
if (ost->formatCtx->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
printf("no find stream info\n");
return -1;
}
//查找解密器
AVCodec *codec = avcodec_find_decoder(ost->formatCtx->streams[0]->codecpar->codec_id);
if (codec == NULL) {
printf("codec not found\n");
return -1;
}
ost->ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(ost->ctx, ost->formatCtx->streams[0]->codecpar);
if (avcodec_open2(ost->ctx, codec, NULL) < 0) {
printf("codec not open\n");
return -1;
}
printf("pix format is %d\n", ost->formatCtx->streams[0]->codecpar->format);//==AVPixelFormat->AV_PIX_FMT_BGRA
ost->frame = alloc_picture((AVPixelFormat)ost->formatCtx->streams[0]->codecpar->format, ost->formatCtx->streams[0]->codecpar->width, ost->formatCtx->streams[0]->codecpar->height);
//图像缩放
ost->img_convert_ctx = sws_getContext(ost->formatCtx->streams[0]->codecpar->width, ost->formatCtx->streams[0]->codecpar->height, (AVPixelFormat)ost->formatCtx->streams[0]->codecpar->format,
ost->enctx->width, ost->enctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
//目标
ost->outframe = alloc_picture(AV_PIX_FMT_YUV420P, ost->enctx->width, ost->enctx->height);
return 0;
}
static int open_audio(MyOutPutStream *ost) {
ost->formatCtx = avformat_alloc_context();
AVInputFormat *ifmt = av_find_input_format("dshow");//设备类型
AVDictionary* options = NULL;
//av_dict_set(&options, "video_size","1920*1080",0);//大小 默认全部
av_dict_set(&options, "framerate", "15", 0);//帧lu
if (avformat_open_input(&ost->formatCtx, "audio=virtual-audio-capturer", ifmt, &options) != 0) {
printf("open input device fail\n");
return -1;
}
av_dict_free(&options);
if (avformat_find_stream_info(ost->formatCtx, NULL) < 0) {
printf("avformat_find_stream_info faill\n");
return -1;
}
if (ost->formatCtx->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
printf("no find stream info\n");
return -1;
}
//查找解密器
AVCodec *codec = avcodec_find_decoder(ost->formatCtx->streams[0]->codecpar->codec_id);
if (codec == NULL) {
printf("codec not found\n");
return -1;
}
ost->ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(ost->ctx, ost->formatCtx->streams[0]->codecpar);
if (avcodec_open2(ost->ctx, codec, NULL) < 0) {
printf("codec not open\n");
return -1;
}
ost->frame = alloc_audio_frame((AVSampleFormat)ost->formatCtx->streams[0]->codecpar->format, ost->formatCtx->streams[0]->codecpar->channel_layout, ost->formatCtx->streams[0]->codecpar->channels, ost->formatCtx->streams[0]->codecpar->sample_rate, ost->formatCtx->streams[0]->codecpar->frame_size);
//目标
ost->ctx->channel_layout = av_get_default_channel_layout(ost->ctx->channels);
ost->swr = swr_alloc();
av_opt_set_int(ost->swr, "in_channel_count", ost->ctx->channels, 0);
av_opt_set_int(ost->swr, "in_channel_layout", ost->ctx->channel_layout, 0);
av_opt_set_int(ost->swr, "in_sample_rate", ost->ctx->sample_rate, 0);
av_opt_set_sample_fmt(ost->swr, "in_sample_fmt", ost->ctx->sample_fmt, 0);
//gai
int64_t desc_channel_layout = ost->enctx->channel_layout;
int64_t desc_channel_count = ost->enctx->channels;
int64_t desc_sample_rate = ost->enctx->sample_rate;
AVSampleFormat desc_fmt = ost->enctx->sample_fmt;
av_opt_set_int(ost->swr, "out_channel_count", desc_channel_count, 0);
av_opt_set_int(ost->swr, "out_channel_layout", desc_channel_layout, 0);
av_opt_set_int(ost->swr, "out_sample_rate", desc_sample_rate, 0);
av_opt_set_sample_fmt(ost->swr, "out_sample_fmt", desc_fmt, 0);
printf("\n声道%d,采样率%d格式%d----》声道%d,采样率%d格式%d\n", ost->ctx->channels, ost->ctx->sample_rate,
ost->ctx->sample_fmt, desc_channel_count, desc_sample_rate, desc_fmt);
int DES_NB_SAMPLES = ost->enctx->frame_size;//每次需要1024个 不够有噪音
ost->outframe = alloc_audio_frame(desc_fmt, desc_channel_layout, desc_channel_count, desc_sample_rate, DES_NB_SAMPLES);
ost->newframe = alloc_audio_frame(desc_fmt, desc_channel_layout, desc_channel_count, desc_sample_rate, 0);
ost->newframe2 = alloc_audio_frame(desc_fmt, desc_channel_layout, desc_channel_count, desc_sample_rate, 0);
ost->fifo = av_audio_fifo_alloc(desc_fmt, desc_channel_count, 10240);
return 0;
}
static AVFrame* getVideoFrame(MyOutPutStream *ost) {
int ret;
while (ost->runing) {
if (ost->gobledata->audiotime == 0) {
av_usleep(20);
continue;
}
if (ost->outframe->pts>0&&ost->nexttps < ost->gobledata->audiotime - 1000000LL * ost->enctx->time_base.num / ost->enctx->time_base.den) {
ost->outframe->pts = ost->nexttps;
ost->nexttps = ost->outframe->pts + 1000000LL * ost->enctx->time_base.num / ost->enctx->time_base.den;
return ost->outframe;
}
if (ost->gobledata->audiotime>0 && ost->nexttps > ost->gobledata->audiotime + 1000000LL * ost->enctx->time_base.num / ost->enctx->time_base.den) {
av_usleep(1000000LL * ost->enctx->time_base.num / ost->enctx->time_base.den);
}
AVPacket packet = { 0 };
av_init_packet(&packet);
if (av_read_frame(ost->formatCtx, &packet) >= 0) {
avcodec_send_packet(ost->ctx, &packet);
if (avcodec_receive_frame(ost->ctx, ost->frame) < 0) {
printf("decode error\n");
}
else {
//printf("采集到图片");
//转换 AV_PIX_FMT_BGRA to AV_PIX_FMT_YUV420P
sws_scale(ost->img_convert_ctx, (const unsigned char* const*)ost->frame->data, ost->frame->linesize, 0, ost->frame->height, ost->outframe->data, ost->outframe->linesize);
if (ost->nexttps == 0) {
ost->outframe->pts = 0; //av_gettime();
}
else { ost->outframe->pts = ost->nexttps; }
ost->nexttps = ost->outframe->pts + 1000000LL * ost->enctx->time_base.num / ost->enctx->time_base.den;
//提取yizhen
}
}
av_packet_unref(&packet);
break;
}
return ost->outframe;
}
static AVFrame* getAudioFrame(MyOutPutStream *ost) {
while (true) {
int size = av_audio_fifo_size(ost->fifo);
if (size >= ost->enctx->frame_size) {//超过
printf("\n第一次超过\n");
size = ost->enctx->frame_size;
av_audio_fifo_read(ost->fifo, (void**)ost->outframe->data, size);
//完整
//写文件 替换
ost->outframe->pts = ost->nexttps;
ost->nexttps = ost->outframe->pts + 1000000LL * ost->outframe->linesize[0] / (ost->enctx->sample_rate*av_get_bytes_per_sample(ost->enctx->sample_fmt));
ost->gobledata->audiotime = ost->outframe->pts;
return ost->outframe;
}
AVPacket packet = { 0 };
av_init_packet(&packet);
if (av_read_frame(ost->formatCtx, &packet) >= 0) {
avcodec_send_packet(ost->ctx, &packet);
if (avcodec_receive_frame(ost->ctx, ost->frame) < 0) {
printf("audio decode error\n");
return NULL;
}
else {
//printf("采集到音频"); //
swr_convert_frame(ost->swr, ost->newframe, ost->frame);
av_audio_fifo_write(ost->fifo, (void**)ost->newframe->data, ost->newframe->nb_samples);
int64_t dealy = swr_get_delay(ost->swr, ost->enctx->sample_rate);
if (dealy > 0) {
swr_convert_frame(ost->swr, ost->newframe2, NULL);
av_audio_fifo_write(ost->fifo, (void**)ost->newframe2->data, ost->newframe2->nb_samples);
}
//这里可以判断下fifo里的数量是否够1024
int size = av_audio_fifo_size(ost->fifo);
if (size < ost->enctx->frame_size) {
//数据不够
av_packet_unref(&packet);
continue;
}
size = ost->enctx->frame_size;//够了
av_audio_fifo_read(ost->fifo, (void**)ost->outframe->data, size);//固定大小的
//写文件 gai
//fwrite(outframe->data[0], size, desc_channel_count*av_get_bytes_per_sample(desc_fmt), file);
if (ost->nexttps == 0) {
ost->outframe->pts = 0;//av_gettime();
}
else { ost->outframe->pts = ost->nexttps; }
ost->nexttps = ost->outframe->pts + 1000000LL * ost->outframe->linesize[0] / (ost->enctx->sample_rate*av_get_bytes_per_sample(ost->enctx->sample_fmt));
ost->gobledata->audiotime = ost->outframe->pts;
}
}
av_packet_unref(&packet);
break;
}
return ost->outframe;
}
static void* read_thread(void* p) {
MyOutPutStream *ost = (MyOutPutStream*)p;
int ret;
ost->runing = 1;
AVFrame* frame = NULL;
while (ost->runing) {
if (ost->enctx->codec_type == AVMEDIA_TYPE_VIDEO) {
frame = getVideoFrame(ost);
}
else {
frame = getAudioFrame(ost);
}
if (frame == NULL) {//error
} else {
int64_t ttt= frame->pts;
{
AVRational base; base.num = 1; base.den = 1000000LL;//帧率
//转换回去去压缩器的时基
frame->pts = av_rescale_q(frame->pts, base, ost->enctx->time_base);
}
//开始压缩
avcodec_send_frame(ost->enctx, frame);
AVPacket pkt = { 0 };
av_init_packet(&pkt);
ret = avcodec_receive_packet(ost->enctx, &pkt);
if (ret == 0) {//需去掉前面4个才是标准h264流
//压缩成功
//printf("压缩%s成功", ost->enctx->codec_type == AVMEDIA_TYPE_VIDEO ? "视频" : "音频");
pushPakcet(ost->packetList,&pkt, ttt);
}
av_packet_unref(&pkt);
}
}
return NULL;
}
static int writefile(AVFormatContext *oc,MyOutPutStream *ost) {
AVPacket pkt;
av_init_packet(&pkt);
int64_t pts = popPacket(ost->packetList, &pkt);
if (pts < 0) { return -1; }
write_frame(oc, &ost->enctx->time_base, ost->st, &pkt);
ost->nextshowpts = pts + 1000000LL * ost->enctx->time_base.num / ost->enctx->time_base.den;
return 0;
}
static int myclose(MyOutPutStream *ost) {
av_frame_free(&ost->frame);
av_frame_free(&ost->newframe);
av_frame_free(&ost->newframe2);
av_frame_free(&ost->outframe);
av_audio_fifo_free(ost->fifo);
avcodec_free_context(&ost->ctx);
avformat_close_input(&ost->formatCtx);
avcodec_free_context(&ost->enctx);
return 0;
}
int main()
{
//采集音视频写入文件或rmtp
int ret=0;
avdevice_register_all();
MyOutPutStream video_st = { 0 };
MyOutPutStream audio_st = { 0 };
GobleData gobledata = { 0 };
video_st.gobledata = &gobledata;
audio_st.gobledata = &gobledata;
const char* filename = "C:\\Users\\lilin\\Desktop\\1.mp4";//"C:\\Users\\lilin\\Desktop\\1.mp4";
AVFormatContext *oc = NULL;
avformat_alloc_output_context2(&oc, NULL, NULL, filename);
if (oc == NULL) {
printf("AVFormatContext init fail");
return -1;
}
AVOutputFormat *fmt = oc->oformat;
ret=add_video(oc,&video_st);
if (ret < 0) {
return - 1;
}
ret=add_audio(oc,&audio_st);
if (ret < 0) {
return -1;
}
av_dump_format(oc, 0, filename, 1);//打印输出流
//打开写头信息
if (!(fmt->flags&AVFMT_NOFILE)) {
ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0) {
printf("无法输出到指定位置");
return -1;
}
}
/*write stram header if any*/
AVDictionary* opt = NULL;
ret = avformat_write_header(oc, &opt);
av_dict_free(&opt);
if (ret < 0) {
printf("open output file error");
return -1;
}
ret=open_video(&video_st);
if (ret < 0) {
return -1;
}
ret=open_audio(&audio_st);
if (ret < 0) {
return -1;
}
//30帧
initPakcetList(&video_st.packetList,300);
initPakcetList(&audio_st.packetList, 1100);
pthread_create(&video_st.pthread,NULL, read_thread,&video_st);
pthread_create(&audio_st.pthread, NULL, read_thread, &audio_st);
ret = 0;
int xunhuan = 15*60;//10s
while (ret==0&&xunhuan>0) {
if (video_st.nextshowpts <= audio_st.nextshowpts &&video_st.packetList->pkgSize>0) {//写入获取下一个视频包并写入文件
printf("写入视频%d", xunhuan);
ret = writefile(oc, &video_st);
xunhuan--;
}
else if (audio_st.packetList->pkgSize > 0) {//写入获取下一个yinping包并写入文件
// printf("写入音频%d", xunhuan);
printf("写入音频%d", xunhuan);
ret = writefile(oc, &audio_st);
}
else {
av_usleep(300);
}
}
video_st.runing = 0;
audio_st.runing = 0;
destoryPakcetList(video_st.packetList);
destoryPakcetList(audio_st.packetList);
pthread_join(video_st.pthread,NULL);
pthread_join(audio_st.pthread, NULL);
//
av_write_trailer(oc);
avformat_close_input(&oc);
myclose(&video_st);
myclose(&audio_st);
//
av_free(video_st.packetList); video_st.packetList = NULL;
av_free(audio_st.packetList); audio_st.packetList = NULL;
getchar();
printf("ok");
return 0;
}
rtmp只需要将地址改为rtmp
avformat_alloc_output_context2(&oc, NULL, “flv”, filename); 这句改成flv
讨论群261074724