ffmpeg拉rtmp流

ffmpeg拉rtmp流并用SDL播放

开发环境
操作系统:win10
IDE:vs2019
ffmpeg版本:5.1

#include <stdio.h>

#define __STDC_CONSTANT_MACROS

#include "libavformat/avformat.h"
#include "libavutil/mathematics.h"
#include "libavutil/time.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include <SDL/SDL.h>


#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio

void notifyGetAudioFrame() {
	SDL_Event sdlEvent;
	//sdlEvent.type = SDL_EVENT_BUFFER_END;
	SDL_PushEvent(&sdlEvent);
}



//Buffer:
//|-----------|-------------|
//chunk-------pos---len-----|
static  Uint8* audio_chunk = 0;
static  Uint32  audio_len = 0;
static  Uint8* audio_pos = 0;

/* 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
*/
static 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  */

	//memcpy(stream, audio_pos, len);
	SDL_MixAudioFormat(stream, audio_pos, AUDIO_S16SYS, len, SDL_MIX_MAXVOLUME);
	//SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
	audio_pos += len;
	audio_len -= len;

	//if (audio_len <= 0) {
	//	// 读取完了,通知继续读取数据
	//	notifyGetAudioFrame();
	//}
}
//-----------------

int main(int argc, char* argv[])
{
	const AVOutputFormat* ofmt = NULL;
	//Input AVFormatContext
	AVFormatContext* ifmt_ctx = NULL;
	AVPacket* pkt;
	const char* in_filename, * out_filename;
	int ret, i;

	int videoindex = -1;
	int audioindex = -1;

	int frame_index = 0;
	in_filename = "rtmp://ns8.indexforce.com/home/mystream";

	int screen_w = 0, screen_h = 0;
	SDL_Window* screen;
	int w, h;
	SDL_Renderer* sdlRenderer;
	SDL_Texture* sdlTexture;
	SDL_Rect sdlRect;

	//视频解码器
	AVCodecContext* pVideoCodecCtx = NULL;
	AVCodecParameters* pVideoCodecPara = NULL;
	AVCodec* pVideoCodec = NULL;

	//音频解码器
	AVCodecContext* pAudioCodecCtx = NULL;
	AVCodecParameters* pAudioCodecPara = NULL;
	AVCodec* pAudioCodec = NULL;

	AVFrame* pFrame = NULL;
	AVFrame* pFrameYUV = NULL;

	struct SwsContext* img_convert_ctx = NULL;

	//Out Audio Param
	int out_nb_samples;
	enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
	int out_sample_rate = 44100;

	//SDL_AudioSpec
	SDL_AudioSpec wanted_spec;

	uint8_t* out_buffer = NULL;

	AVChannelLayout outChannelLayout = AV_CHANNEL_LAYOUT_STEREO;
	AVChannelLayout inChannelLayout;

	SDL_AudioDeviceID deviceId;

	FILE* pFile = NULL;
	fopen_s(&pFile, "output.pcm", "wb");

	//Network
	avformat_network_init();
	//Input
	if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
		printf("Could not open input file.");
		goto end;
	}
	if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
		printf("Failed to retrieve input stream information");
		goto end;
	}

	for (i = 0; i < ifmt_ctx->nb_streams; i++)
		if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
			videoindex = i;
			break;
		}

	for (i = 0; i < ifmt_ctx->nb_streams; i++)
		if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
			audioindex = i;
			break;
		}

	pVideoCodecPara = ifmt_ctx->streams[videoindex]->codecpar;
	pVideoCodec = (AVCodec*)avcodec_find_decoder(pVideoCodecPara->codec_id);
	if (pVideoCodec == NULL)
	{
		printf("Codec not found.\n");
		goto end;
	}

	pVideoCodecCtx = avcodec_alloc_context3(pVideoCodec);
	avcodec_parameters_to_context(pVideoCodecCtx, pVideoCodecPara);
	if (pVideoCodecCtx == NULL) {
		printf("Cannot alloc context.");
		goto end;
	}

	if (avcodec_open2(pVideoCodecCtx, pVideoCodec, NULL) < 0)
	{
		printf("Could not open codec.\n");
		goto end;
	}

	pAudioCodecPara = ifmt_ctx->streams[audioindex]->codecpar;
	pAudioCodec = (AVCodec*)avcodec_find_decoder(pAudioCodecPara->codec_id);
	if (pAudioCodec == NULL)
	{
		printf("Codec not found.\n");
		goto end;
	}

	pAudioCodecCtx = avcodec_alloc_context3(pAudioCodec);
	avcodec_parameters_to_context(pAudioCodecCtx, pAudioCodecPara);
	if (pAudioCodecCtx == NULL) {
		printf("Cannot alloc context.");
		goto end;
	}

	if (avcodec_open2(pAudioCodecCtx, pAudioCodec, NULL) < 0)
	{
		printf("Could not open codec.\n");
		goto end;
	}

	av_dump_format(ifmt_ctx, 0, in_filename, 0);

	screen_w = 640;
	screen_h = 480;
	screen = SDL_CreateWindow("B_Play", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
	if (!screen)
	{
		printf("SDL: could not create window - exiting:%s\n", SDL_GetError());
		return -1;
	}
	sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
	sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, screen_w, screen_h);
	sdlRect.x = 0;
	sdlRect.y = 0;
	sdlRect.w = screen_w;
	sdlRect.h = screen_h;

	pkt = av_packet_alloc();
	pFrame = av_frame_alloc();
	pFrameYUV = av_frame_alloc();

	pFrameYUV->width = pVideoCodecCtx->width;
	pFrameYUV->height = pVideoCodecCtx->height;
	pFrameYUV->format = AV_PIX_FMT_YUV420P;
	av_frame_get_buffer(pFrameYUV, 1);

	img_convert_ctx = sws_getContext(pVideoCodecCtx->width, pVideoCodecCtx->height, pVideoCodecCtx->pix_fmt,
		screen_w, screen_h, AV_PIX_FMT_YUV420P,
		SWS_BICUBIC, NULL, NULL, NULL);

	//SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
	SDL_Init(SDL_INIT_TIMER | SDL_INIT_AUDIO);

	out_nb_samples = pAudioCodecCtx->frame_size;
	//out_sample_rate = pAudioCodecCtx->sample_rate;
	wanted_spec.freq = out_sample_rate;
	wanted_spec.format = AUDIO_S16SYS;
	wanted_spec.channels = 2;
	wanted_spec.silence = 0;
	wanted_spec.samples = out_nb_samples;
	wanted_spec.callback = fill_audio;
	wanted_spec.userdata = NULL;

	if ((deviceId = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, NULL, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE)) < 2) {
		return-1;
	}

	out_buffer = (uint8_t*)av_malloc(out_nb_samples * 4);

	//outChannelLayout = AV_CHANNEL_LAYOUT_STEREO;
	inChannelLayout = pAudioCodecCtx->ch_layout;
	outChannelLayout.nb_channels = 2;
	inChannelLayout.nb_channels = pAudioCodecCtx->ch_layout.nb_channels;
	struct SwrContext* au_convert_ctx;
	au_convert_ctx = swr_alloc();
	swr_alloc_set_opts2(&au_convert_ctx, &outChannelLayout, out_sample_fmt, out_sample_rate,
		&inChannelLayout, pAudioCodecCtx->sample_fmt, pAudioCodecCtx->sample_rate, 0, NULL);
	swr_init(au_convert_ctx);

	SDL_PauseAudioDevice(deviceId, 0);

	while (1) {
		//Get an AVPacket
		ret = av_read_frame(ifmt_ctx, pkt);
		if (ret < 0)
			break;
		if (pkt->stream_index == videoindex) {
			if (avcodec_send_packet(pVideoCodecCtx, pkt) == 0) {
				while (avcodec_receive_frame(pVideoCodecCtx, pFrame) == 0) {
					sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pVideoCodecCtx->height,
						pFrameYUV->data, pFrameYUV->linesize);
								
					SDL_UpdateYUVTexture(sdlTexture, &sdlRect,
						pFrameYUV->data[0], pFrameYUV->linesize[0],
						pFrameYUV->data[1], pFrameYUV->linesize[1],
						pFrameYUV->data[2], pFrameYUV->linesize[2]);

					SDL_RenderClear(sdlRenderer);
					SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
					SDL_RenderPresent(sdlRenderer);

					SDL_Delay(40);
				}
			}
			i++;//只计算视频帧
		}
		else if (pkt->stream_index == audioindex) {
			if (avcodec_send_packet(pAudioCodecCtx, pkt) == 0) {
				while (avcodec_receive_frame(pAudioCodecCtx, pFrame) == 0) {
					int nRet = swr_convert(au_convert_ctx, &out_buffer, out_nb_samples, (const uint8_t**)pFrame->data, pFrame->nb_samples);

					int buffSize = av_samples_get_buffer_size(NULL, 2, nRet, out_sample_fmt, 1);

					fwrite(out_buffer, 1, buffSize, pFile);

					while (audio_len > 0)//Wait until finish
						SDL_Delay(1);
					//Set audio buffer (PCM data)
					audio_chunk = (Uint8*)out_buffer;
					//Audio buffer length
					audio_len = buffSize;

					audio_pos = audio_chunk;

					//av_packet_unref(pkt);//重置pkt的内容
				}
			}
		}

		av_packet_unref(pkt);

	}
	av_packet_free(&pkt);

end:

	if (pFrame) {
		av_frame_free(&pFrame);
	}
	if (pFrameYUV) {
		av_frame_free(&pFrameYUV);
	}

	if (pVideoCodecCtx) {
		avcodec_close(pVideoCodecCtx);
	}

	if (pAudioCodecCtx) {
		avcodec_close(pAudioCodecCtx);
	}

	SDL_CloseAudio();//Close SDL
	SDL_Quit();

	avformat_close_input(&ifmt_ctx);
	if (ret < 0 && ret != AVERROR_EOF) {
		printf("Error occurred.\n");
		return -1;
	}
	return 0;
}

视频播放没有问题
音频单独保存成pcm文件播放没问题,但是用SDL直接播放会有电流等杂音,原因还没有找到。

完善

代码进行了重构,音频播放也没有问题了。
完整工程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

都市无名者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值