ffmpeg音视频同步
程序员文章站
2022-07-02 08:52:09
...
没有实现,可能是时间戳同步问题
#include <stdio.h>
#include<iostream>
#include<Windows.h>
#define __STDC_CONSTANT_MACROS
#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/avassert.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavutil/imgutils.h"
#include "libavutil/time.h"
#include "libavutil/avstring.h"
#include "libswresample/swresample.h"
#include "SDL.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
#include <SDL/SDL.h>
#ifdef __cplusplus
};
#endif
#endif
/*
FIX: H.264 in some container format (FLV, MP4, MKV etc.) need
"h264_mp4toannexb" bitstream filter (BSF)
*Add SPS,PPS in front of IDR frame
*Add start code ("0,0,0,1") in front of NALU
H.264 in some container (MPEG2TS) don't need this BSF.
*/
//'1': Use H.264 Bitstream Filter
#define USE_H264BSF 0
/*
FIX:AAC in some container format (FLV, MP4, MKV etc.) need
"aac_adtstoasc" bitstream filter (BSF)
*/
//'1': Use AAC Bitstream Filter
#define USE_AACBSF 0
#define USE_SDL 1
#define SWR_CH_MAX 32
#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
/* The output bit rate in bit/s */
#define OUTPUT_BIT_RATE 96000
/* The number of output channels */
#define OUTPUT_CHANNELS 2
//Refresh Event
#define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)
#define SFM_BREAK_EVENT (SDL_USEREVENT + 2)
int thread_exit = 0;
#pragma region print info function
//Show Dshow Device
void show_dshow_device() {
AVFormatContext *ifmt_ctx = avformat_alloc_context();
AVDictionary* options = NULL;
av_dict_set(&options, "list_devices", "true", 0);
AVInputFormat *iformat = av_find_input_format("dshow");
printf("========Device Info=============\n");
avformat_open_input(&ifmt_ctx, "video=dummy", iformat, &options);
printf("================================\n");
}
//Show Dshow Device Option
void show_dshow_device_option() {
AVFormatContext *ifmt_ctx = avformat_alloc_context();
AVDictionary* options = NULL;
av_dict_set(&options, "list_options", "true", 0);
AVInputFormat *iformat = av_find_input_format("dshow");
printf("========Device Option Info======\n");
avformat_open_input(&ifmt_ctx, "video=USB2.0 PC CAMERA", iformat, &options);
printf("================================\n");
}
//Show VFW Device
void show_vfw_device() {
AVFormatContext *ifmt_ctx = avformat_alloc_context();
AVInputFormat *iformat = av_find_input_format("vfwcap");
printf("========VFW Device Info======\n");
avformat_open_input(&ifmt_ctx, "list", iformat, NULL);
printf("=============================\n");
}
//Show AVFoundation Device
void show_avfoundation_device() {
AVFormatContext *ifmt_ctx = avformat_alloc_context();
AVDictionary* options = NULL;
av_dict_set(&options, "list_devices", "true", 0);
AVInputFormat *iformat = av_find_input_format("avfoundation");
printf("==AVFoundation Device Info===\n");
avformat_open_input(&ifmt_ctx, "", iformat, &options);
printf("=============================\n");
}
#pragma endregion
int sfp_refresh_thread(void *opaque)
{
thread_exit = 0;
while (!thread_exit) {
SDL_Event event;
event.type = SFM_REFRESH_EVENT;
SDL_PushEvent(&event);
SDL_Delay(40);
}
thread_exit = 0;
//Break
SDL_Event event;
event.type = SFM_BREAK_EVENT;
SDL_PushEvent(&event);
return 0;
}
static Uint8 *audio_chunk;
static Uint32 audio_len;
static Uint8 *audio_pos;
/* The audio function callback takes the following parameters:
* stream: A pointer to the audio buffer to be filled
* len: The length (in bytes) of the audio buffer
*/
void fill_audio(void *udata, Uint8 *stream, int len) {
//SDL 2.0
SDL_memset(stream, 0, len);
if (audio_len == 0)
return;
len = (len>audio_len ? audio_len : len); /* Mix as much data as possible */
SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
audio_pos += len;
audio_len -= len;
}
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;
}
//setup_array函数摘自ffmpeg例程
static void setup_array(uint8_t* out[SWR_CH_MAX], AVFrame* in_frame, int format, int samples)
{
if (av_sample_fmt_is_planar((AVSampleFormat)format))
{
int i; int plane_size = av_get_bytes_per_sample((AVSampleFormat)(format & 0xFF)) * samples; format &= 0xFF;
//从decoder出来的frame中的data数据不是连续分布的,所以不能这样写:
in_frame->data[0] + i*plane_size;
for (i = 0; i < in_frame->channels; i++)
{
out[i] = in_frame->data[i];
}
}
else
{
out[0] = in_frame->data[0];
}
}
int main(int argc, char* argv[])
{
AVFormatContext *ifmt_ctx_video = NULL;
AVFormatContext *ifmt_ctx_audio = NULL;
AVCodecContext *inCodecCtx_v;
AVCodecContext *inCodecCtx_a;
int in_videoindex;
int in_audioindex;
AVCodec *in_video_Codec = NULL;
AVCodec *in_audio_Codec = NULL;
AVAudioFifo *fifo = NULL;
AVFormatContext *ofmt_ctx = NULL;
AVOutputFormat *ofmt = NULL;
AVCodecContext *outCodecCtx_v = NULL;
AVCodecContext *outCodecCtx_a = NULL;
int out_videoindex;
int out_audioindex;
AVStream *out_video_st = NULL;
AVStream *out_audio_st = NULL;
AVCodec *out_video_Codec = NULL;
AVCodec *out_audio_Codec = NULL;
//AVPacket pkt;
int ret = -1;
int got_frame = -1;
int got_packet = -1;
int64_t cur_pts_v = 0, cur_pts_a = 0;
static int64_t audio_pts = 0;
int frame_index = 0;
int frame_index_a = 0;
//const char *out_filename = "rtmp://localhost:1935/live/room";;
const char *out_filename = "d:/test33.flv";
int64_t start_time = 0;
//av_register_all();
avformat_network_init();
//Register Device
avdevice_register_all();
//Show Dshow Device
show_dshow_device();
//Show Device Options
show_dshow_device_option();
//Show VFW Options
show_vfw_device();
AVInputFormat *ifmt = av_find_input_format("dshow");
#pragma region open camera
//分配视频输入上下文
ifmt_ctx_video = avformat_alloc_context();
//Set own video device's name
char * psCameraName = dup_wchar_to_utf8(L"video=USB2.0 PC CAMERA");
if (avformat_open_input(&ifmt_ctx_video, "video=USB2.0 PC CAMERA", ifmt, NULL) != 0) {
printf("Couldn't open input stream.(无法打开视频输入流)\n");
return -1;
}
if (avformat_find_stream_info(ifmt_ctx_video, NULL) < 0)
{
printf("Couldn't find stream information.(无法获取流信息)\n");
return -1;
}
in_videoindex = -1;
for (int i = 0; i < ifmt_ctx_video->nb_streams; i++)
{
if (ifmt_ctx_video->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
in_videoindex = i;
break;
}
}
if (in_videoindex == -1)
{
printf("Couldn't find a video stream.(没有找到视频流)\n");
return -1;
}
in_video_Codec = avcodec_find_decoder(ifmt_ctx_video->streams[in_videoindex]->codecpar->codec_id);
inCodecCtx_v = avcodec_alloc_context3(in_video_Codec);
avcodec_parameters_to_context(inCodecCtx_v, ifmt_ctx_video->streams[in_videoindex]->codecpar);
if (in_video_Codec == NULL)
{
printf("Codec not found.(无法找到视频解码器编码)\n");
return -1;
}
if (avcodec_open2(inCodecCtx_v, in_video_Codec, NULL) < 0)
{
printf("Could not open codec.(无法打开视频解码器)\n");
return -1;
}
//Dump Format video------------
av_dump_format(ifmt_ctx_video, 0, "video=USB2.0 PC CAMERA", 0);
#pragma endregion
#pragma region open mic
ifmt_ctx_audio = avformat_alloc_context();
char * psMicDevName = dup_wchar_to_utf8(L"audio=麦克风 (USB2.0 MIC)");
if (avformat_open_input(&ifmt_ctx_audio, psMicDevName, ifmt, NULL) != 0) {
printf("Couldn't open input stream.(无法打开输入流)\n");
return -1;
}
AVDictionary* pOptions = NULL;
ifmt_ctx_audio->probesize = 1 * 1024;
ifmt_ctx_audio->max_analyze_duration = 1 * AV_TIME_BASE;
if (avformat_find_stream_info(ifmt_ctx_audio, &pOptions) < 0)
{
printf("Couldn't find stream information.(无法获取流信息)\n");
return -1;
}
in_audioindex = -1;
for (int i = 0; i < ifmt_ctx_audio->nb_streams; i++)
if (ifmt_ctx_audio->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
in_audioindex = i;
break;
}
if (in_audioindex == -1)
{
printf("Couldn't find a video stream.(没有找到视频流)\n");
return -1;
}
in_audio_Codec = avcodec_find_decoder(ifmt_ctx_audio->streams[in_audioindex]->codecpar->codec_id);
if (in_audio_Codec == NULL)
{
printf("Codec not found.(无法找到视频解码器编码)\n");
return -1;
}
inCodecCtx_a = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(inCodecCtx_a, ifmt_ctx_audio->streams[in_audioindex]->codecpar);
if (avcodec_open2(inCodecCtx_a, in_audio_Codec, NULL) < 0)
{
printf("Could not open codec.(无法打开视频解码器)\n");
return -1;
}
//Dump Format audio------------
av_dump_format(ifmt_ctx_audio, 0, psMicDevName, 0);
#pragma endregion
#pragma region output
avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_filename);
if (!ofmt_ctx) {
printf("Could not create output context(不能创建输出上下文)\n");
return -1;
}
ofmt = ofmt_ctx->oformat;
if (ifmt_ctx_video->streams[in_videoindex]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
//set codec context param
out_video_Codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!out_video_Codec) {
printf("can not find encoder !\n");
return -1;
}
outCodecCtx_v = avcodec_alloc_context3(out_video_Codec);
if (!outCodecCtx_v) {
printf("can not alloc context!\n");
return -1;
}
// take first format from list of supported formats
outCodecCtx_v->pix_fmt = AV_PIX_FMT_YUV422P;
outCodecCtx_v->height = ifmt_ctx_video->streams[in_videoindex]->codecpar->height;
outCodecCtx_v->width = ifmt_ctx_video->streams[in_videoindex]->codecpar->width;
outCodecCtx_v->sample_aspect_ratio = inCodecCtx_a->sample_aspect_ratio; // 采样宽高比:像素宽/像素高
outCodecCtx_v->time_base.num = 1;
outCodecCtx_v->time_base.den = 25;
outCodecCtx_v->framerate = inCodecCtx_v->framerate;
outCodecCtx_v->bit_rate = 400000;
outCodecCtx_v->gop_size = 250;
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
outCodecCtx_v->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
outCodecCtx_v->qmin = 10;
outCodecCtx_v->qmax = 51;
outCodecCtx_v->max_b_frames = 3;
//Optional Param
AVDictionary *param = 0;
av_dict_set(¶m, "preset", "ultrafast", 0);
//解码时花屏,加上有花屏,去掉有延时
av_dict_set(¶m, "tune", "zerolatency", 0);
//av_dict_set(¶m, "rtbufsize", 3041280 , 0);
if (avcodec_open2(outCodecCtx_v, out_video_Codec, ¶m) < 0) {
printf("Failed to open encoder! (编码器打开失败!)\n");
return -1;
}
out_video_st = avformat_new_stream(ofmt_ctx, out_video_Codec);
if (!out_video_st)
{
printf("can not new stream for output!\n");
return -1;
}
out_video_st->time_base.num = 1;
out_video_st->time_base.den = 25;
//video_st->codec = oCodecCtx;
avcodec_parameters_from_context(out_video_st->codecpar, outCodecCtx_v);
out_videoindex = out_video_st->index;
}
if (ifmt_ctx_audio->streams[in_audioindex]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
/*
for (int i = 0; i < ifmt_ctx_audio->nb_streams; i++) {
//根据输入流创建输出流(Create output AVStream according to input AVStream)
AVStream *in_stream = ifmt_ctx_audio->streams[i];
AVCodec *codec = NULL;
codec = avcodec_find_decoder(in_stream->codecpar->codec_id);
AVStream *out_stream = avformat_new_stream(ofmt_ctx, codec);
if (!out_stream) {
printf("Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto end;
}
out_audioindex = out_stream->index;
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
ret = avcodec_parameters_to_context(codec_ctx, in_stream->codecpar);
if (ret < 0)
{
printf("Failed to copy in_stream codecpar to codec context\n");
goto end;
}
codec_ctx->codec_tag = 0;
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
ret = avcodec_parameters_from_context(out_stream->codecpar, codec_ctx);
out_stream->codecpar->codec_tag = 0;
if (ret < 0)
{
printf("Failed to copy codec context to out_stream codecpar context\n");
goto end;
}
//avcodec_free_context(&codec_ctx);
}
*/
//set codec context param
out_audio_Codec = avcodec_find_encoder(AV_CODEC_ID_MP3);
if (!out_audio_Codec) {
printf("can not find encoder !\n");
return -1;
}
//初始化音频输出流
//out_audio_st = avformat_new_stream(ofmt_ctx, out_audio_Codec);
out_audio_st = avformat_new_stream(ofmt_ctx, out_audio_Codec);
if (!out_audio_st)
{
printf("can not new stream for output!\n");
return -1;
}
out_audioindex = out_audio_st->index;
//分配音频输出上下文
outCodecCtx_a = avcodec_alloc_context3(out_audio_Codec);
if (!outCodecCtx_a) {
printf("can not alloc context!\n");
return -1;
}
avcodec_parameters_to_context(outCodecCtx_a, out_audio_st->codecpar);
//set codec context param
//use default audio encoder
//use the input audio encoder
outCodecCtx_a->sample_rate = inCodecCtx_a->sample_rate;
outCodecCtx_a->channel_layout = inCodecCtx_a->channel_layout;
outCodecCtx_a->channels = av_get_channel_layout_nb_channels(inCodecCtx_a->channel_layout);
if (outCodecCtx_a->channel_layout == 0)
{
outCodecCtx_a->channel_layout = AV_CH_LAYOUT_STEREO;
outCodecCtx_a->channels = av_get_channel_layout_nb_channels(outCodecCtx_a->channel_layout);
}
//AV_SAMPLE_FMT_FLTP
outCodecCtx_a->sample_fmt = AV_SAMPLE_FMT_S16P;
//outCodecCtx_a->sample_fmt = out_audio_Codec->sample_fmts[0];
//AVRational time_base = { 1, inCodecCtx_a->sample_rate };
//out_audio_st->time_base = time_base;
//AVRational time_base = { 1, ifmt_ctx_audio->streams[in_audioindex]->codecpar->sample_rate };
//out_audio_st->time_base = time_base;
//out_audioindex = out_audio_st->index;
//outCodecCtx_a->time_base = time_base;
outCodecCtx_a->codec_tag = 0;
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
outCodecCtx_a->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
if (avcodec_open2(outCodecCtx_a, out_audio_Codec, 0) < 0)
{
//编码器打开失败,退出程序
return -1;
}
avcodec_parameters_from_context(out_audio_st->codecpar,outCodecCtx_a);
out_audio_st->codecpar->codec_tag = 0;
}
if (avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_READ_WRITE) < 0)
{
printf("can not open output file handle!\n");
return -1;
}
if (avformat_write_header(ofmt_ctx, NULL) < 0)
{
printf("can not write the header of the output file!\n");
return -1;
}
//Dump Format------------------
av_dump_format(ofmt_ctx, 0, out_filename, 1);
#pragma endregion
//prepare before decode and encode
AVPacket *dec_packet = (AVPacket *)av_malloc(sizeof(AVPacket));
AVPacket enc_packet;
#pragma region prepare image
//
////AVPacket pkt;
//int y_size = oCodecCtx->width * oCodecCtx->height;
//av_new_packet(&enc_packet, y_size * 3);
//转码上下文
struct SwsContext *img_convert_ctx;
img_convert_ctx = sws_getContext(inCodecCtx_v->width, inCodecCtx_v->height,
inCodecCtx_v->pix_fmt, outCodecCtx_v->width, outCodecCtx_v->height, AV_PIX_FMT_YUV422P, SWS_BICUBIC, NULL, NULL, NULL);
struct SwsContext *img_convert_ctx_SDL;
img_convert_ctx_SDL = sws_getContext(inCodecCtx_v->width, inCodecCtx_v->height,
inCodecCtx_v->pix_fmt, outCodecCtx_v->width, outCodecCtx_v->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
AVFrame *pFrame_vedio, *pFrameYUV, *pFrameYUV_SDL;
pFrameYUV = av_frame_alloc();
pFrameYUV_SDL = av_frame_alloc();
//设置帧的格式、宽度和高度,否则会出现
//1.AVFrame.format is not set
//2.AVFrame.width or height is not set
pFrameYUV->format = outCodecCtx_v->pix_fmt;
pFrameYUV->width = outCodecCtx_v->width;
pFrameYUV->height = outCodecCtx_v->height;
pFrameYUV_SDL->format = AV_PIX_FMT_YUV420P;
pFrameYUV_SDL->width = outCodecCtx_v->width;
pFrameYUV_SDL->height = outCodecCtx_v->height;
// 存储图像数据
uint8_t *out_buffer_vedio;
// av_image_get_buffer_size:返回使用给定参数存储图像所需的数据量的字节大小
out_buffer_vedio = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV422P, outCodecCtx_v->width, outCodecCtx_v->height, 1));
// 根据指定的图像参数和提供的数组设置数据指针和线条(data pointers and linesizes)
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer_vedio, AV_PIX_FMT_YUV422P, outCodecCtx_v->width, outCodecCtx_v->height, 1);
// 存储图像数据
uint8_t *out_buffer_SDL;
// av_image_get_buffer_size:返回使用给定参数存储图像所需的数据量的字节大小
out_buffer_SDL = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, outCodecCtx_v->width, outCodecCtx_v->height, 1));
// 根据指定的图像参数和提供的数组设置数据指针和线条(data pointers and linesizes)
av_image_fill_arrays(pFrameYUV_SDL->data, pFrameYUV_SDL->linesize, out_buffer_SDL, AV_PIX_FMT_YUV420P, outCodecCtx_v->width, outCodecCtx_v->height, 1);
#pragma endregion
#pragma region prepare Mic
//音频参数
if (!(fifo = av_audio_fifo_alloc(outCodecCtx_a->sample_fmt, outCodecCtx_a->channels, 1)))
{
fprintf(stderr, "Could not allocate FIFO\n");
return AVERROR(ENOMEM);
}
//Swr
struct SwrContext *au_convert_ctx;
au_convert_ctx = swr_alloc();
au_convert_ctx = swr_alloc_set_opts(au_convert_ctx,
av_get_default_channel_layout(outCodecCtx_a->channels), AV_SAMPLE_FMT_S16P, 44100,
av_get_default_channel_layout(inCodecCtx_a->channels), inCodecCtx_a->sample_fmt, inCodecCtx_a->sample_rate,
0,
NULL);
swr_init(au_convert_ctx);
//Out Audio Param
uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
//AAC:1024 MP3:1152
int out_nb_samples = inCodecCtx_a->frame_size;
//FIX:Some Codec's Context Information is missing
int64_t in_channel_layout = av_get_default_channel_layout(inCodecCtx_a->channels);
AVFrame *pFrame_audio, *pFrameMP3;
pFrameMP3 = av_frame_alloc();
//设置帧的格式、宽度和高度,否则会出现
//1.AVFrame.format is not set
//2.AVFrame.width or height is not set
pFrameMP3->format = outCodecCtx_a->pix_fmt;
pFrameMP3->nb_samples = outCodecCtx_a->frame_size;
int stream_mapping_size = ifmt_ctx_audio->nb_streams;
int *stream_mapping = (int *)av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));
if (!stream_mapping) {
ret = AVERROR(ENOMEM);
goto end;
}
#pragma endregion
#if USE_SDL
//SDL----------------------------
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
printf("Could not initialize SDL - %s\n", SDL_GetError());
return -1;
}
int screen_w = 0, screen_h = 0;
SDL_Window *screen;
screen_w = outCodecCtx_v->width;
screen_h = outCodecCtx_v->height;
//screen = SDL_SetVideoMode(screen_w, screen_h, 0, 0);
//SDL 2.0 Support for multiple windows
screen = SDL_CreateWindow("Simplest FFmpeg Device", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
screen_w, screen_h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!screen) {
printf("SDL: could not set video mode - exiting:%s\n", SDL_GetError());
return -1;
}
SDL_Texture *sdlTexture;
//bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen);
SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
Uint32 pixformat = 0;
//IYUV: Y + U + V (3 planes)
//YV12: Y + V + U (3 planes)
pixformat = SDL_PIXELFORMAT_IYUV;
sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat, SDL_TEXTUREACCESS_STREAMING, outCodecCtx_v->width, outCodecCtx_v->height);
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = screen_w;
rect.h = screen_h;
//SDL End------------------------
//------------------------------
SDL_Thread *video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);
//
SDL_Event event;
//SDL_AudioSpec wanted_spec;
////SDL_AudioSpec
//wanted_spec.freq = 44100;
//wanted_spec.format = AUDIO_S16SYS;
//wanted_spec.channels = av_get_default_channel_layout(outCodecCtx_a->channels);
//wanted_spec.silence = 0;
//wanted_spec.samples = out_nb_samples;
//wanted_spec.callback = fill_audio;
//wanted_spec.userdata = inCodecCtx_a;
//if (SDL_OpenAudio(&wanted_spec, NULL)<0) {
// printf("can't open audio.\n");
// return -1;
//}
#endif // USE_SDL
start_time = av_gettime();
while (1) {
//Wait
SDL_WaitEvent(&event);
if (event.type == SFM_REFRESH_EVENT)
{
av_init_packet(dec_packet);
//Get an AVPacket
if (av_compare_ts(cur_pts_v, ifmt_ctx_video->streams[in_videoindex]->time_base, cur_pts_a, ifmt_ctx_audio->streams[in_audioindex]->time_base) < 0)
{
if (cur_pts_v > 10000)
{
thread_exit = 1;
}
got_frame = -1;
got_packet = -1;
if (av_read_frame(ifmt_ctx_video, dec_packet) >= 0)
{
if (dec_packet->stream_index == in_videoindex)
{
pFrame_vedio = av_frame_alloc();
if (!pFrame_vedio) {
printf("alloc pFrame Failed.\n");
return -1;
}
//ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
ret = avcodec_send_packet(inCodecCtx_v, dec_packet);
got_frame = avcodec_receive_frame(inCodecCtx_v, pFrame_vedio);
if (ret < 0)
{
av_frame_free(&pFrame_vedio);
printf("Decode Error.\n");
return -1;
}
if (got_frame == 0)
{
//转码成YUV格式
sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame_vedio->data, pFrame_vedio->linesize, 0, inCodecCtx_v->height, pFrameYUV->data, pFrameYUV->linesize);
sws_scale(img_convert_ctx_SDL, (const unsigned char* const*)pFrame_vedio->data, pFrame_vedio->linesize, 0, inCodecCtx_v->height, pFrameYUV_SDL->data, pFrameYUV_SDL->linesize);
//初始化封装包
enc_packet.data = NULL;
enc_packet.size = 0;
av_init_packet(&enc_packet);
//编码
//ret = avcodec_encode_video2(oCodecCtx, &enc_packet, pFrameYUV, &got_encpicture);
ret = avcodec_send_frame(outCodecCtx_v, pFrameYUV);
//FIX : non-strictly-monotonic PTS
AVRational time_base_in = { 1, AV_TIME_BASE }; //{ 1, 1000000 };
AVRational time_base_conert = outCodecCtx_v->time_base;//{ 1, 1000 };
pFrameYUV->pts = av_rescale_q(dec_packet->pts, time_base_in, time_base_conert);
if (ret < 0)
{
av_frame_free(&pFrame_vedio);
printf("Encode Error.\n");
return -1;
}
got_packet = avcodec_receive_packet(outCodecCtx_v, &enc_packet);
if (got_packet == 0)
{
enc_packet.stream_index = out_video_st->index;
//FIX:No PTS (Example: Raw H.264)
//Simple Write PTS
if (enc_packet.pts == AV_NOPTS_VALUE)
{
//Write PTS
AVRational time_base1 = ofmt_ctx->streams[out_videoindex]->time_base;
//Duration between 2 frames (us)
int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(ofmt_ctx->streams[out_videoindex]->r_frame_rate);
//Parameters
enc_packet.pts = (double)(frame_index*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
enc_packet.dts = enc_packet.pts;
enc_packet.duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
}
if (dec_packet->stream_index == in_videoindex)
{
//Delay
AVRational time_base = { 1, AV_TIME_BASE };//{ 1, 1000 };
AVRational time_base_q = ofmt_ctx->streams[out_videoindex]->time_base;
int64_t pts_time = av_rescale_q(dec_packet->dts, time_base, time_base_q);
int64_t now_time = av_rescale_q(av_gettime(), time_base, time_base_q);
if (pts_time > now_time)
av_usleep(pts_time - now_time);
}
//Write PTS
AVRational time_base = ofmt_ctx->streams[out_videoindex]->time_base;//{ 1, 1000 };
AVRational r_framerate1 = ifmt_ctx_video->streams[in_videoindex]->r_frame_rate;// { 50, 2 };
AVRational time_base_q = { 1, AV_TIME_BASE };
//Duration between 2 frames (us)
int64_t calc_duration = (double)(AV_TIME_BASE)*(1 / av_q2d(r_framerate1));
enc_packet.pts = av_rescale_q(frame_index*calc_duration, time_base_q, time_base);
enc_packet.dts = enc_packet.pts;
enc_packet.duration = av_rescale_q(calc_duration, time_base_q, time_base); //(double)(calc_duration)*(double)(av_q2d(time_base_q)) / (double)(av_q2d(time_base));
enc_packet.pos = -1;
//Print to Screen
if (enc_packet.stream_index == in_videoindex) {
frame_index++;
printf("%8d Send video frames to output URL\n", frame_index);
}
cur_pts_v = enc_packet.pts;
//写到输出上下文
ret = av_interleaved_write_frame(ofmt_ctx, &enc_packet);
}
av_packet_unref(&enc_packet);
}
av_frame_free(&pFrame_vedio);
}
av_packet_unref(dec_packet);
#if USE_SDL
//int y_size = iCodecCtx->width * iCodecCtx->height;
////SDL---------------------------
//// 设置纹理数据
//pFrameYUV_SDL->data[0] = out_buffer; // Y
//pFrameYUV_SDL->data[1] = out_buffer + y_size; // U
//pFrameYUV_SDL->data[2] = out_buffer + y_size * 3 / 2; // V
#if 0
SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);
#else
SDL_UpdateYUVTexture(sdlTexture, &rect,
pFrameYUV_SDL->data[0], pFrameYUV_SDL->linesize[0],
pFrameYUV_SDL->data[1], pFrameYUV_SDL->linesize[1],
pFrameYUV_SDL->data[2], pFrameYUV_SDL->linesize[2]);
#endif
// 清理渲染器
SDL_RenderClear(sdlRenderer);
// 将纹理数据copy到渲染器
//将sdlRect区域的图像显示到dstsdlRect区域
//SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect );
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &rect);
// 显示
SDL_RenderPresent(sdlRenderer);
//SDL End-----------------------
#endif
}
}
else
{
const int output_frame_size = outCodecCtx_a->frame_size;
//int finished = 0;
av_init_packet(dec_packet);
dec_packet->data = NULL;
dec_packet->size = 0;
got_frame = -1;
got_packet = -1;
if (av_read_frame(ifmt_ctx_audio, dec_packet) >= 0)
{
if (dec_packet->stream_index == in_audioindex)
{
pFrame_audio = av_frame_alloc();
if (!pFrame_audio)
{
printf("alloc pFrame Failed.\n");
return -1;
}
if (dec_packet->stream_index >= stream_mapping_size ||
stream_mapping[dec_packet->stream_index] < 0) {
av_packet_unref(dec_packet);
continue;
}
//ret = avcodec_decode_audio4(inCodecCtx_a, pFrame, &got_picture, packet);
int ret = avcodec_send_packet(inCodecCtx_a, dec_packet);
got_frame = avcodec_receive_frame(inCodecCtx_a, pFrame_audio);
if (ret < 0)
{
av_frame_free(&pFrame_audio);
printf("Decode Error.\n");
return -1;
}
if (got_frame == 0)
{
//写入fifo
while (true)
{
int fifo_size = av_audio_fifo_size(fifo);
if (fifo_size >= output_frame_size)
break;
uint8_t **converted_input_samples;
converted_input_samples = (uint8_t **)calloc(outCodecCtx_a->channels,
sizeof(converted_input_samples));
ret = av_samples_alloc(converted_input_samples, NULL,
outCodecCtx_a->channels,
pFrame_audio->nb_samples,
outCodecCtx_a->sample_fmt, 0);
//swr_convert(au_convert_ctx, pFrameMP3->data, pFrameMP3->nb_samples, (const uint8_t**)m_ain, pFrame_audio->nb_samples);
int conver_num = swr_convert(au_convert_ctx,
converted_input_samples, pFrame_audio->nb_samples,
(const uint8_t**)pFrame_audio->extended_data, pFrame_audio->nb_samples);
ret = av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + pFrame_audio->nb_samples);
ret = av_audio_fifo_write(fifo, (void **)converted_input_samples, conver_num);
}
av_init_packet(&enc_packet);
enc_packet.data = NULL;
enc_packet.size = 0;
float sumlen = av_audio_fifo_size(fifo);
int num = 0;
//从fifo读出
while (true)
{
int fifo_size = av_audio_fifo_size(fifo);
if (fifo_size < output_frame_size)
break;
const int frame_size = FFMIN(av_audio_fifo_size(fifo),
outCodecCtx_a->frame_size);
AVFrame *covert_frame;
covert_frame = av_frame_alloc();
covert_frame->nb_samples = output_frame_size;
covert_frame->channel_layout = outCodecCtx_a->channel_layout;
covert_frame->format = outCodecCtx_a->sample_fmt;
covert_frame->sample_rate = outCodecCtx_a->sample_rate;
ret = av_frame_get_buffer(covert_frame, 0);
ret = av_audio_fifo_read(fifo, (void **)covert_frame->data, frame_size);
if (covert_frame)
{
covert_frame->pts = audio_pts;
audio_pts += covert_frame->nb_samples;
}
ret = avcodec_send_frame(outCodecCtx_a, covert_frame);
got_packet = avcodec_receive_packet(outCodecCtx_a, &enc_packet);
if (got_packet == 0)
{
frame_index_a++;
AVRational timebase = {1,AV_TIME_BASE };
int64_t cal_duration = AV_TIME_BASE *(float)covert_frame->nb_samples / (float)covert_frame->sample_rate;
enc_packet.pts = av_rescale_q_rnd(audio_pts, outCodecCtx_a->time_base,
ofmt_ctx->streams[out_audio_st->index]->time_base,
(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
enc_packet.dts = enc_packet.pts;
enc_packet.duration = av_rescale_q_rnd(cal_duration, timebase,
ofmt_ctx->streams[out_audio_st->index]->time_base,
(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
//int64_t cal_duration = AV_TIME_BASE *(float)covert_frame->nb_samples / (float)covert_frame->sample_rate;
//enc_packet.pos = -1;
//enc_packet.pts = frame_index_a*cal_duration;
//enc_packet.dts = enc_packet.pts;
//enc_packet.duration = cal_duration;
AVRational time_base = ofmt_ctx->streams[out_audioindex]->time_base;//{ 1, 1000 };
AVRational time_base_q = { 1, AV_TIME_BASE };
//Delay
int64_t pts_time = av_rescale_q(enc_packet.pts, time_base, time_base_q);
int64_t now_time = av_gettime() - start_time;
if (pts_time > now_time)
av_usleep(pts_time - now_time);
//Print to Screen
if (enc_packet.stream_index == in_audioindex)
{
printf("Send %8d audio frames to output URL\n", frame_index_a);
}
cur_pts_a = enc_packet.pts;
ret = av_interleaved_write_frame(ofmt_ctx, &enc_packet);
}
av_frame_free(&covert_frame);
}
av_packet_unref(&enc_packet);
av_packet_unref(dec_packet);
av_frame_free(&pFrame_audio);
}
}
}
av_packet_unref(dec_packet);
}
}
else if (event.type == SDL_QUIT)
{
thread_exit = 1;
}
else if(event.type == SFM_BREAK_EVENT)
{
break;
}
}
SDL_Quit();
//写文件尾(Write file trailer)
av_write_trailer(ofmt_ctx);
//av_free(out_buffer_vedio);
//av_free(out_buffer_audio);
end:
if (out_video_st)
avcodec_close(outCodecCtx_v);
if (out_audio_st)
avcodec_close(outCodecCtx_a);
avformat_close_input(&ifmt_ctx_video);
avformat_close_input(&ifmt_ctx_audio);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_close(ofmt_ctx->pb);
avformat_free_context(ifmt_ctx_video);
avformat_free_context(ifmt_ctx_audio);
avformat_free_context(ofmt_ctx);
if (ret < 0 && ret != AVERROR_EOF) {
printf("Error occurred.\n");
return -1;
}
return 0;
}
上一篇: ASP中Session技巧 默认过期时间为20分钟
下一篇: FFmpeg音视频同步