首页 > 其他分享 >ffmpeg拉rtmp流并用SDL播放

ffmpeg拉rtmp流并用SDL播放

时间:2023-05-17 13:57:08浏览次数:48  
标签:audio ffmpeg ctx len rtmp SDL NULL out

开发环境
操作系统: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直接播放会有电流等杂音,原因还没有找到。

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

标签:audio,ffmpeg,ctx,len,rtmp,SDL,NULL,out
From: https://www.cnblogs.com/kn-zheng/p/17408519.html

相关文章

  • Qt编写视频监控系统73-不同视频流不同类型的判断和解析(http/m3u8/rtsp/rtmp等)
    一、前言这套视频监控系统大概从2018年起步整体框架,一步步积累到现在,中间经历了无数次的各种视频文件、视频流、视频设备的播放测试,比如光视频文件就有mp4/wmv/rmvb/mkv/avi等格式,视频设备有本地USB摄像头、桌面等,视频流有rtmp/rtsp/rtp/http等,其中http开头的就有视频文件和视频......
  • Ubuntu 使用 apt 安装 ffmpeg
    网上很多教程安装ffmpeg的时候,都是使用源码包进行编译安装,比较麻烦,还要安装一堆额外的东西。但看官网,已经提供了Ubuntu的FFmpeg包,可以直接使用apt进行安装。(ffmpeg官网https://www.ffmpeg.org/download.html)安装过程:1、更新apt:sudoaptupdate2、安装FFmpeg:sudoaptinstall......
  • Qt ffmpeg yolov5 tensorrt 高性能部署,使用tensorrt推理yolov5模型,封装成了dll, 支
    Qtffmpegyolov5tensorrt高性能部署,使用tensorrt推理yolov5模型,封装成了dll,支持多窗口多线程推理,本项目为4窗口版,各个窗口支持识别类别,阈值,roi区域等设置。算法支持onnxruntime,tensorrt推理,以及推理加deepsort,bytetrack和kcf多目标跟踪。ID:353200676908443403......
  • 录屏软件Captura+FFmpeg
    Captura下载:https://github.com/MathewSachin/Captura/releases/tag/v8.0.0FFmpeg下载:https://ffmpeg.org/download.html......
  • ffmpeg编译配置srt模块
    官网拉取srt源码gitclonehttps://github.com/Haivision/srt.gitsudoapt-getupdatesudoapt-getupgradesudoapt-getinstalltclshpkg-configcmakelibssl-devbuild-essentialzlib1g-devgit打开目录进行编译默认的安装路径是/usr/local,默认静态动态一起编译。./conf......
  • Windows上搭建rtsp-simple-server流媒体服务器实现rtsp、rtmp等推流以及转流、前端htm
    上面讲了Nginx-http-flv-module+flv.js进行流媒体服务器搭建和前端播放视频流的过程。但是Nginx-http-flv-module对于Windows的支持以及推流格式的支持优先,所以下面推荐rtsp-simple-server流媒体服务器的使用。rtsp-simple-serverhttps://github.com/aler9/rtsp-simple-serverrtsp-......
  • ffmpeg下载和安装
    官网:https://ffmpeg.org下载wgethttps://ffmpeg.org/releases/ffmpeg-6.0.tar.gztar-zxvfffmpeg-6.0.tar.gz准备工作安装X264库yuminstall-ygit(如果已安装GIT则忽略)gitclonehttps://code.videolan.org/videolan/x264.gitcdx264#执行./configure--enable-sha......
  • Qt推流程序(视频文件/视频流/摄像头/桌面转成流媒体rtmp+hls+webrtc)可在网页和播放器远
    一、前言说明推流直播就是把采集阶段封包好的内容传输到服务器的过程。其实就是将现场的视频信号从手机端,电脑端,摄影机端打包传到服务器的过程。“推流”对网络要求比较高,如果网络不稳定,直播效果就会很差,观众观看直播时就会发生卡顿等现象,观看体验比较糟糕。主流推送协议有RTMP、......
  • 头部证券公司安全体系搭建实战讲解—开源网安S-SDLC平台助力金融科技安全发展
    数字化时代背景下,新兴技术广泛应用导致软件安全隐患不断扩大。而金融行业由于项目周期长、业务规模大、应用数量多、合规监管严、内外合作多等特性,进一步加重了安全风险。与此同时,《等保2.0》、《网络安全法》等国家政策的发布,自上而下推动信息安全发展,对金融机构的安全防护提出更......
  • rodert教你学FFmpeg实战这一篇就够了
    rodert教你学FFmpeg实战这一篇就够了建议收藏,以备查阅pdf阅读版:链接:pan.baidu.com/s/11kIaq5V6…提取码:java[toc]前言todo有人问rodert哥这篇文章干货有多干,问就是,硌牙。ffmpeg有多强大,我想你都知道了,现在很多市场上的剪辑软件都是基于它做的,只是加了一些包装。读完本篇,你会发现......