vs版本:2017 ffmpeg版本号: ffmpeg version N-102642-g864d1ef2fc Copyright ? 2000-2021 the FFmpeg developers built with gcc 8.1.0 (x86_64-win32-seh-rev0, Built by MinGW-W64 project) configuration: --arch=x86_64 --prefix=/home/ffmpeg_static_x64 --disable-debug libavutil 57. 0.100 / 57. 0.100 libavcodec 59. 1.100 / 59. 1.100 libavformat 59. 2.101 / 59. 2.101 libavdevice 59. 0.100 / 59. 0.100 libavfilter 8. 0.101 / 8. 0.101 libswscale 6. 0.100 / 6. 0.100 libswresample 4. 0.100 / 4. 0.100
关于ffmpeg的lib和dll,本人在csdn上上传了相关资源,并且免费下载。
桌面录制时,发现有一些暂不了解的地方,比如avformat_write_header后,time_base发生了改变,这个使得av_packet_rescale_ts(&packet, pCodecCtx_Video->time_base, pFormatCtx_Out->streams[0]->time_base);这句特别重要,若没这句,视频播放速度可能会很快。 今天先在此记录,后面若有其他发现,会继续更新此博客。 代码如下:
#include "stdafx.h"
#include <Windows.h>
#include <conio.h>
#ifdef __cplusplus
extern "C"
{
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/imgutils.h"
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avdevice.lib")
#pragma comment(lib, "avfilter.lib")
#pragma comment(lib, "swscale.lib")
#ifdef __cplusplus
};
#endif
AVFormatContext *pFormatCtx_Video = NULL, *pFormatCtx_Out = NULL;
AVCodecContext *pCodecCtx_Video = NULL;
AVCodec *pCodec_Video = NULL;
AVFifoBuffer *fifo_video = NULL;
int VideoIndex;
AVCodecContext *pCodecEncodeCtx_Video = NULL;
AVCodec *pCodecEncode_Video = NULL;
SwsContext *img_convert_ctx;
int frame_size = 0;
uint8_t *picture_buf = NULL, *frame_buf = NULL;
bool bCap = true;
DWORD WINAPI ScreenCapThreadProc(LPVOID lpParam);
int OpenVideoCapture()
{
const AVInputFormat *ifmt = av_find_input_format("gdigrab");
AVDictionary *options = NULL;
av_dict_set(&options, "framerate", "25", NULL);
av_dict_set(&options, "probesize", "50000000", NULL);
if (avformat_open_input(&pFormatCtx_Video, "desktop", ifmt, &options) != 0)
{
printf("Couldn't open input stream.(无法打开视频输入流)\n");
return -1;
}
if (avformat_find_stream_info(pFormatCtx_Video, NULL)<0)
{
printf("Couldn't find stream information.(无法获取视频流信息)\n");
return -1;
}
if (pFormatCtx_Video->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
{
printf("Couldn't find video stream information.(无法获取视频流信息)\n");
return -1;
}
pCodec_Video = (AVCodec *)avcodec_find_decoder(pFormatCtx_Video->streams[0]->codecpar->codec_id);
pCodecCtx_Video = avcodec_alloc_context3(pCodec_Video);
if (pCodec_Video == NULL)
{
printf("Codec not found.(没有找到解码器)\n");
return -1;
}
if (avcodec_open2(pCodecCtx_Video, pCodec_Video, NULL) < 0)
{
printf("Could not open codec.(无法打开解码器)\n");
return -1;
}
pCodecCtx_Video->bit_rate = 400000;
pCodecCtx_Video->width = 1920;
pCodecCtx_Video->height = 1080;
AVRational timeBase;
timeBase.num = 1;
timeBase.den = 25;
pCodecCtx_Video->time_base = timeBase;
AVRational frameRate;
frameRate.den = 1;
frameRate.num = 25;
pCodecCtx_Video->framerate = frameRate;
pCodecCtx_Video->gop_size = 25;
pCodecCtx_Video->max_b_frames = 1;
pCodecCtx_Video->pix_fmt = AV_PIX_FMT_YUV420P;
img_convert_ctx = sws_getContext(pCodecCtx_Video->width, pCodecCtx_Video->height, (AVPixelFormat)pFormatCtx_Video->streams[0]->codecpar->format,
pCodecCtx_Video->width, pCodecCtx_Video->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
frame_size = av_image_get_buffer_size(pCodecCtx_Video->pix_fmt, pCodecCtx_Video->width, pCodecCtx_Video->height, 1);
fifo_video = av_fifo_alloc(30 * av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx_Video->width, pCodecCtx_Video->height, 1));
return 0;
}
static char *dup_wchar_to_utf8(wchar_t *w)
{
char *s = NULL;
int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
s = (char *)av_malloc(l);
if (s)
WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0);
return s;
}
int OpenOutPut()
{
AVStream *pVideoStream = NULL, *pAudioStream = NULL;
const char *outFileName = "test.mp4";
avformat_alloc_output_context2(&pFormatCtx_Out, NULL, NULL, outFileName);
if (pFormatCtx_Video->streams[0]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
VideoIndex = 0;
pVideoStream = avformat_new_stream(pFormatCtx_Out, NULL);
if (!pVideoStream)
{
printf("can not new stream for output!\n");
return -1;
}
AVRational timeBase;
timeBase.num = 1;
timeBase.den = 50;
pVideoStream->time_base = timeBase;
pCodecEncode_Video = (AVCodec *)avcodec_find_encoder(pFormatCtx_Out->oformat->video_codec);
if (!(pCodecEncode_Video)) {
fprintf(stderr, "Could not find encoder for '%s'\n",
avcodec_get_name(AV_CODEC_ID_MPEG4));
exit(1);
}
pCodecEncodeCtx_Video = avcodec_alloc_context3(pCodecEncode_Video);
if (!pCodecEncodeCtx_Video) {
fprintf(stderr, "Could not alloc an encoding context\n");
exit(1);
}
pCodecEncodeCtx_Video->time_base = timeBase;
pCodecEncodeCtx_Video->codec_id = pFormatCtx_Out->oformat->video_codec;
pCodecEncodeCtx_Video->bit_rate = 400000;
pCodecEncodeCtx_Video->width = 1920;
pCodecEncodeCtx_Video->height = 1080;
pCodecEncodeCtx_Video->gop_size = 25;
pCodecEncodeCtx_Video->pix_fmt = AV_PIX_FMT_YUV420P;
if ((avcodec_open2(pCodecEncodeCtx_Video, pCodecEncode_Video, NULL)) < 0)
{
printf("can not open the encoder\n");
return -1;
}
}
if (!(pFormatCtx_Out->oformat->flags & AVFMT_NOFILE))
{
if (avio_open2(&pFormatCtx_Out->pb, outFileName, AVIO_FLAG_WRITE, nullptr, nullptr) < 0)
{
printf("can not open output file handle!\n");
return -1;
}
}
pCodecCtx_Video->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx_Video->codec_id = pFormatCtx_Out->oformat->video_codec;
pCodecCtx_Video->pix_fmt = AV_PIX_FMT_YUV420P;
avcodec_parameters_from_context(pVideoStream->codecpar, pCodecCtx_Video);
int iWriteResult = avformat_write_header(pFormatCtx_Out, NULL);
if (iWriteResult < 0)
{
printf("can not write the header of the output file!\n");
return -1;
}
return 0;
}
int Yuv420ToFile(AVFrame *avFrame, int width, int height, FILE *pFile)
{
if (!pFile || !avFrame)
{
return -1;
}
for (int j = 0; j < height; j++)
{
fwrite(avFrame->data[0] + j * avFrame->linesize[0], 1, width, pFile);
}
for (int j = 0; j < height / 2; j++)
{
fwrite(avFrame->data[1] + j * avFrame->linesize[1], 1, width / 2, pFile);
}
for (int j = 0; j < height / 2; j++)
{
fwrite(avFrame->data[2] + j * avFrame->linesize[2], 1, width / 2, pFile);
}
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
avdevice_register_all();
if (OpenVideoCapture() < 0)
{
return -1;
}
if (OpenOutPut() < 0)
{
return -1;
}
AVFrame *pFrame = av_frame_alloc();
AVFrame *pFrameYUV = av_frame_alloc();
int frame_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx_Video->width, pCodecCtx_Video->height, 1);
uint8_t *out_buffer_yuv420 = (uint8_t *)av_malloc(frame_size);
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer_yuv420, AV_PIX_FMT_YUV420P, pCodecCtx_Video->width, pCodecCtx_Video->height, 1);
static int iPicCount = 0;
AVPacket packet = { 0 };
for (;;)
{
av_packet_unref(&packet);
if (av_read_frame(pFormatCtx_Video, &packet) < 0)
{
break;
}
int ret = 0;
if (packet.stream_index == VideoIndex)
{
ret = avcodec_send_packet(pCodecCtx_Video, &packet);
iPicCount++;
while (ret >= 0)
{
ret = avcodec_receive_frame(pCodecCtx_Video, pFrame);
if (ret == AVERROR(EAGAIN))
{
break;
}
else if (ret == AVERROR_EOF)
{
return 0;
}
else if (ret < 0) {
fprintf(stderr, "Error during decoding\n");
exit(1);
}
int iScale = sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecEncodeCtx_Video->height, pFrameYUV->data, pFrameYUV->linesize);
packet.pts = iPicCount;
packet.dts = iPicCount;
av_packet_rescale_ts(&packet, pCodecCtx_Video->time_base, pFormatCtx_Out->streams[0]->time_base);
pFrameYUV->width = pFrame->width;
pFrameYUV->height = pFrame->height;
pFrameYUV->format = AV_PIX_FMT_YUV420P;
pFrameYUV->pts = packet.pts;
pFrameYUV->pkt_dts = packet.pts;
av_packet_unref(&packet);
ret = avcodec_send_frame(pCodecEncodeCtx_Video, pFrameYUV);
ret = avcodec_receive_packet(pCodecEncodeCtx_Video, &packet);
ret = av_interleaved_write_frame(pFormatCtx_Out, &packet);
avio_flush(pFormatCtx_Out->pb);
}
if (iPicCount > 1000)
{
break;
}
if (ret == AVERROR(EAGAIN))
{
continue;
}
}
}
printf("main end\n");
av_write_trailer(pFormatCtx_Out);
avio_close(pFormatCtx_Out->pb);
avformat_free_context(pFormatCtx_Out);
return 0;
}
|