mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-06-12 19:24:22 +08:00
播放器新增支持音频:#945
This commit is contained in:
126
player/AudioSRC.cpp
Normal file
126
player/AudioSRC.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
||||
*
|
||||
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
||||
*
|
||||
* Use of this source code is governed by MIT license that can be found in the
|
||||
* LICENSE file in the root of the source tree. All contributing project authors
|
||||
* may be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "Util/logger.h"
|
||||
#include "AudioSRC.h"
|
||||
#include "SDLAudioDevice.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace toolkit;
|
||||
|
||||
AudioSRC::AudioSRC(AudioSRCDelegate *del) {
|
||||
_delegate = del;
|
||||
}
|
||||
|
||||
AudioSRC::~AudioSRC() {}
|
||||
|
||||
void AudioSRC::setOutputAudioConfig(const SDL_AudioSpec &cfg) {
|
||||
int freq = _delegate->getPCMSampleRate();
|
||||
int format = _delegate->getPCMFormat();
|
||||
int channels = _delegate->getPCMChannel();
|
||||
if (-1 == SDL_BuildAudioCVT(&_audio_cvt, format, channels, freq, cfg.format, cfg.channels, cfg.freq)) {
|
||||
throw std::runtime_error("the format conversion is not supported");
|
||||
}
|
||||
InfoL << "audio cvt origin format, freq:" << freq << ", format:" << hex << format << dec << ", channels:" << channels;
|
||||
InfoL << "audio cvt info, "
|
||||
<< "needed:" << _audio_cvt.needed
|
||||
<< ", src_format:" << hex << _audio_cvt.src_format
|
||||
<< ", dst_format:" << _audio_cvt.dst_format << dec
|
||||
<< ", rate_incr:" << _audio_cvt.rate_incr
|
||||
<< ", len_mult:" << _audio_cvt.len_mult
|
||||
<< ", len_ratio:" << _audio_cvt.len_ratio;
|
||||
}
|
||||
|
||||
void AudioSRC::setEnableMix(bool flag) {
|
||||
_enabled = flag;
|
||||
}
|
||||
|
||||
int AudioSRC::getPCMData(char *buf, int size) {
|
||||
if (!_enabled) {
|
||||
return 0;
|
||||
}
|
||||
if (!_audio_cvt.needed) {
|
||||
//获取原始数据,不需要频率转换
|
||||
return _delegate->getPCMData(buf, size);
|
||||
}
|
||||
|
||||
if ((int)(size / _audio_cvt.len_ratio) != _origin_size) {
|
||||
_origin_size = size / _audio_cvt.len_ratio;
|
||||
_origin_buf.reset(new char[std::max(_origin_size, size)], [](char *ptr) {
|
||||
delete[] ptr;
|
||||
});
|
||||
InfoL << "origin pcm buffer size is:" << _origin_size << ", target pcm buffer size is:" << size;
|
||||
}
|
||||
|
||||
auto origin_size = _delegate->getPCMData(_origin_buf.get(), _origin_size);
|
||||
if (!origin_size) {
|
||||
//获取数据失败
|
||||
TraceL << "get empty pcm data";
|
||||
return 0;
|
||||
}
|
||||
|
||||
_audio_cvt.buf = (Uint8 *) _origin_buf.get();
|
||||
_audio_cvt.len = origin_size;
|
||||
if (0 != SDL_ConvertAudio(&_audio_cvt)) {
|
||||
WarnL << "SDL_ConvertAudio failed!";
|
||||
_audio_cvt.len_cvt = 0;
|
||||
}
|
||||
if (_audio_cvt.len_cvt) {
|
||||
_target_buf.append(_origin_buf.get(), _audio_cvt.len_cvt);
|
||||
}
|
||||
if (_target_buf.size() < size) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(buf, _target_buf.data(), size);
|
||||
_target_buf.erase(0, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AudioPlayer::AudioPlayer() : AudioSRC(this) {
|
||||
_device = SDLAudioDevice::Instance().shared_from_this();
|
||||
}
|
||||
|
||||
AudioPlayer::~AudioPlayer() {
|
||||
_device->delChannel(this);
|
||||
}
|
||||
|
||||
void AudioPlayer::setup(int sample_rate, int channel, SDL_AudioFormat format) {
|
||||
_sample_rate = sample_rate;
|
||||
_channel = channel;
|
||||
_format = format;
|
||||
_device->addChannel(this);
|
||||
}
|
||||
|
||||
SDL_AudioFormat AudioPlayer::getPCMFormat() {
|
||||
return _format;
|
||||
}
|
||||
|
||||
int AudioPlayer::getPCMSampleRate() {
|
||||
return _sample_rate;
|
||||
}
|
||||
|
||||
int AudioPlayer::getPCMChannel() {
|
||||
return _channel;
|
||||
}
|
||||
|
||||
int AudioPlayer::getPCMData(char *buf, int size) {
|
||||
if (_buffer.size() < size) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(buf, _buffer.data(), size);
|
||||
_buffer.erase(0, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
void AudioPlayer::inputFrame(const char *data, size_t size){
|
||||
_buffer.append(data, size);
|
||||
}
|
||||
86
player/AudioSRC.h
Normal file
86
player/AudioSRC.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
||||
*
|
||||
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
||||
*
|
||||
* Use of this source code is governed by MIT license that can be found in the
|
||||
* LICENSE file in the root of the source tree. All contributing project authors
|
||||
* may be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef AUDIOSRC_H_
|
||||
#define AUDIOSRC_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "SDL2/SDL.h"
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#pragma comment(lib,"SDL2.lib")
|
||||
#endif //defined(_WIN32)
|
||||
|
||||
#include "Network/Buffer.h"
|
||||
#include "SDLAudioDevice.h"
|
||||
#include "FFMpegDecoder.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace toolkit;
|
||||
|
||||
class AudioSRCDelegate {
|
||||
public:
|
||||
virtual ~AudioSRCDelegate() {};
|
||||
virtual SDL_AudioFormat getPCMFormat() = 0;
|
||||
virtual int getPCMSampleRate() = 0;
|
||||
virtual int getPCMChannel() = 0;
|
||||
virtual int getPCMData(char *buf, int size) = 0;
|
||||
};
|
||||
|
||||
//该类实现pcm的重采样
|
||||
class AudioSRC {
|
||||
public:
|
||||
typedef std::shared_ptr<AudioSRC> Ptr;
|
||||
AudioSRC(AudioSRCDelegate *);
|
||||
virtual ~AudioSRC();
|
||||
|
||||
void setEnableMix(bool flag);
|
||||
void setOutputAudioConfig(const SDL_AudioSpec &cfg);
|
||||
int getPCMData(char *buf, int size);
|
||||
|
||||
private:
|
||||
bool _enabled = true;
|
||||
int _origin_size = 0;
|
||||
std::shared_ptr<char> _origin_buf;
|
||||
AudioSRCDelegate *_delegate = nullptr;
|
||||
BufferLikeString _target_buf;
|
||||
SDL_AudioCVT _audio_cvt;
|
||||
};
|
||||
|
||||
class AudioPlayer : public AudioSRC, private AudioSRCDelegate{
|
||||
public:
|
||||
AudioPlayer();
|
||||
~AudioPlayer() override;
|
||||
|
||||
void setup(int sample_rate, int channel, SDL_AudioFormat format);
|
||||
void inputFrame(const char *data, size_t size);
|
||||
|
||||
private:
|
||||
SDL_AudioFormat getPCMFormat() override;
|
||||
int getPCMSampleRate() override;
|
||||
int getPCMChannel() override;
|
||||
int getPCMData(char *buf, int size) override;
|
||||
|
||||
private:
|
||||
int _sample_rate, _channel;
|
||||
SDL_AudioFormat _format;
|
||||
BufferLikeString _buffer;
|
||||
SDLAudioDevice::Ptr _device;
|
||||
};
|
||||
|
||||
#endif /* AUDIOSRC_H_ */
|
||||
110
player/CMakeLists.txt
Normal file
110
player/CMakeLists.txt
Normal file
@@ -0,0 +1,110 @@
|
||||
find_package(PkgConfig QUIET)
|
||||
|
||||
#查找SDL2是否安装
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(SDL2 QUIET IMPORTED_TARGET sdl2)
|
||||
if (SDL2_FOUND)
|
||||
include_directories(${SDL2_INCLUDE_DIRS})
|
||||
link_directories(${SDL2_LIBRARY_DIRS})
|
||||
list(APPEND LINK_LIB_LIST ${SDL2_LIBRARIES})
|
||||
message(STATUS "found library:${SDL2_LIBRARIES}")
|
||||
endif ()
|
||||
else ()
|
||||
find_package(SDL2 QUIET)
|
||||
if (SDL2_FOUND)
|
||||
include_directories(${SDL2_INCLUDE_DIR})
|
||||
list(APPEND LINK_LIB_LIST ${SDL2_LIBRARY})
|
||||
message(STATUS "found library:${SDL2_LIBRARY}")
|
||||
endif (SDL2_FOUND)
|
||||
endif ()
|
||||
|
||||
#查找ffmpeg/libutil是否安装
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(AVUTIL QUIET IMPORTED_TARGET libavutil)
|
||||
if (AVUTIL_FOUND)
|
||||
include_directories(${AVUTIL_INCLUDE_DIRS})
|
||||
link_directories(${AVUTIL_LIBRARY_DIRS})
|
||||
list(APPEND LINK_LIB_LIST ${AVUTIL_LIBRARIES})
|
||||
message(STATUS "found library:${AVUTIL_LIBRARIES}")
|
||||
endif ()
|
||||
else ()
|
||||
find_package(AVUTIL QUIET)
|
||||
if (AVUTIL_FOUND)
|
||||
include_directories(${AVUTIL_INCLUDE_DIR})
|
||||
list(APPEND LINK_LIB_LIST ${AVUTIL_LIBRARIES})
|
||||
message(STATUS "found library:${AVUTIL_LIBRARIES}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
#查找ffmpeg/libswresample是否安装
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(SWRESAMPLE QUIET IMPORTED_TARGET libswresample)
|
||||
if (SWRESAMPLE_FOUND)
|
||||
include_directories(${SWRESAMPLE_INCLUDE_DIRS})
|
||||
link_directories(${SWRESAMPLE_LIBRARY_DIRS})
|
||||
list(APPEND LINK_LIB_LIST ${SWRESAMPLE_LIBRARIES})
|
||||
message(STATUS "found library:${SWRESAMPLE_LIBRARIES}")
|
||||
endif ()
|
||||
else ()
|
||||
find_package(SWRESAMPLE QUIET)
|
||||
if (SWRESAMPLE_FOUND)
|
||||
include_directories(${SWRESAMPLE_INCLUDE_DIR})
|
||||
list(APPEND LINK_LIB_LIST ${SWRESAMPLE_LIBRARIES})
|
||||
message(STATUS "found library:${SWRESAMPLE_LIBRARIES}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
#查找ffmpeg/libavcodec是否安装
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(AVCODEC QUIET IMPORTED_TARGET libavcodec)
|
||||
if (AVCODEC_FOUND)
|
||||
include_directories(${AVCODEC_INCLUDE_DIRS})
|
||||
link_directories(${AVCODEC_LIBRARY_DIRS})
|
||||
list(APPEND LINK_LIB_LIST ${AVCODEC_LIBRARIES})
|
||||
message(STATUS "found library:${AVCODEC_LIBRARIES}")
|
||||
endif ()
|
||||
else ()
|
||||
find_package(AVCODEC QUIET)
|
||||
if (AVCODEC_FOUND)
|
||||
include_directories(${AVCODEC_INCLUDE_DIR})
|
||||
list(APPEND LINK_LIB_LIST ${AVCODEC_LIBRARIES})
|
||||
message(STATUS "found library:${AVCODEC_LIBRARIES}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
set(PLAYER_NAME "player")
|
||||
|
||||
#如果ffmpeg/libavcodec ffmpeg/libavcodec SDL 都安装了则编译播放器
|
||||
if (SDL2_FOUND AND AVCODEC_FOUND AND AVUTIL_FOUND AND SWRESAMPLE_FOUND)
|
||||
message(STATUS "${PLAYER_NAME} enabled")
|
||||
else ()
|
||||
message(STATUS "${PLAYER_NAME} disabled, please install sdl2 ffmpeg/libavcodec ffmpeg/libavutil ffmpeg/libswresample")
|
||||
endif ()
|
||||
|
||||
aux_source_directory(. SRC_LIST)
|
||||
add_executable(${PLAYER_NAME} ${SRC_LIST})
|
||||
|
||||
if (MSVC)
|
||||
set_target_properties(${PLAYER_NAME} PROPERTIES COMPILE_FLAGS ${VS_FALGS})
|
||||
set_target_properties(${PLAYER_NAME} PROPERTIES LINK_FLAGS "/SAFESEH:NO /SUBSYSTEM:WINDOWS")
|
||||
endif ()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
target_link_libraries(${PLAYER_NAME} -Wl,--start-group ${LINK_LIB_LIST} -Wl,--end-group)
|
||||
else ()
|
||||
target_link_libraries(${PLAYER_NAME} ${LINK_LIB_LIST})
|
||||
endif ()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
312
player/FFMpegDecoder.cpp
Normal file
312
player/FFMpegDecoder.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
||||
*
|
||||
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
||||
*
|
||||
* Use of this source code is governed by MIT license that can be found in the
|
||||
* LICENSE file in the root of the source tree. All contributing project authors
|
||||
* may be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "FFMpegDecoder.h"
|
||||
#define MAX_DELAY_SECOND 60
|
||||
|
||||
using namespace std;
|
||||
using namespace mediakit;
|
||||
|
||||
static string ffmpeg_err(int errnum){
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||
return errbuf;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<bool decoder = true, typename ...ARGS>
|
||||
AVCodec *getCodecByName(ARGS ...names);
|
||||
|
||||
template<bool decoder = true, typename ...ARGS>
|
||||
AVCodec *getCodecByName(const char *name) {
|
||||
auto codec = decoder ? avcodec_find_decoder_by_name(name) : avcodec_find_encoder_by_name(name);
|
||||
if (codec) {
|
||||
InfoL << (decoder ? "got decoder:" : "got encoder:") << name;
|
||||
}
|
||||
return codec;
|
||||
}
|
||||
|
||||
template<bool decoder = true, typename ...ARGS>
|
||||
AVCodec *getCodecByName(const char *name, ARGS ...names) {
|
||||
auto codec = getCodecByName<decoder>(names...);
|
||||
if (codec) {
|
||||
return codec;
|
||||
}
|
||||
return getCodecByName<decoder>(name);
|
||||
}
|
||||
|
||||
template<bool decoder = true>
|
||||
AVCodec *getCodec(enum AVCodecID id) {
|
||||
auto codec = decoder ? avcodec_find_decoder(id) : avcodec_find_encoder(id);
|
||||
if (codec) {
|
||||
InfoL << (decoder ? "got decoder:" : "got encoder:") << avcodec_get_name(id);
|
||||
}
|
||||
return codec;
|
||||
}
|
||||
|
||||
template<bool decoder = true, typename ...ARGS>
|
||||
AVCodec *getCodec(enum AVCodecID id, ARGS ...names) {
|
||||
auto codec = getCodecByName<decoder>(names...);
|
||||
if (codec) {
|
||||
return codec;
|
||||
}
|
||||
return getCodec<decoder>(id);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FFmpegFrame::FFmpegFrame(std::shared_ptr<AVFrame> frame) {
|
||||
if (frame) {
|
||||
_frame = std::move(frame);
|
||||
} else {
|
||||
_frame.reset(av_frame_alloc(), [](AVFrame *ptr) {
|
||||
av_frame_unref(ptr);
|
||||
av_frame_free(&ptr);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
FFmpegFrame::~FFmpegFrame(){
|
||||
if (_data) {
|
||||
delete[] _data;
|
||||
_data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AVFrame *FFmpegFrame::get() const{
|
||||
return _frame.get();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FFmpegSwr::FFmpegSwr(AVSampleFormat output, int channel, int channel_layout, int samplerate){
|
||||
_target_format = output;
|
||||
_target_channels = channel;
|
||||
_target_channel_layout = channel_layout;
|
||||
_target_samplerate = samplerate;
|
||||
_frame_pool.setSize(8);
|
||||
}
|
||||
|
||||
FFmpegSwr::~FFmpegSwr(){
|
||||
if (_ctx) {
|
||||
swr_free(&_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
FFmpegFrame::Ptr FFmpegSwr::inputFrame(const FFmpegFrame::Ptr &frame){
|
||||
if (frame->get()->format == _target_format &&
|
||||
frame->get()->channels == _target_channels &&
|
||||
frame->get()->channel_layout == _target_channel_layout &&
|
||||
frame->get()->sample_rate == _target_samplerate) {
|
||||
//不转格式
|
||||
return frame;
|
||||
}
|
||||
if (!_ctx) {
|
||||
_ctx = swr_alloc_set_opts(nullptr, _target_channel_layout, _target_format, _target_samplerate,
|
||||
frame->get()->channel_layout, (AVSampleFormat) frame->get()->format,
|
||||
frame->get()->sample_rate, 0, nullptr);
|
||||
InfoL << "swr_alloc_set_opts:" << av_get_sample_fmt_name((enum AVSampleFormat) frame->get()->format) << " -> "
|
||||
<< av_get_sample_fmt_name(_target_format);
|
||||
}
|
||||
if (_ctx) {
|
||||
FFmpegFrame::Ptr out = _frame_pool.obtain();
|
||||
out->get()->format = _target_format;
|
||||
out->get()->channel_layout = _target_channel_layout;
|
||||
out->get()->channels = _target_channels;
|
||||
out->get()->sample_rate = _target_samplerate;
|
||||
out->get()->pkt_dts = frame->get()->pkt_dts;
|
||||
out->get()->pkt_pts = frame->get()->pkt_pts;
|
||||
out->get()->pts = frame->get()->pts;
|
||||
|
||||
int ret;
|
||||
if(0 != (ret = swr_convert_frame(_ctx, out->get(), frame->get()))){
|
||||
WarnL << "swr_convert_frame failed:" << ffmpeg_err(ret);
|
||||
return nullptr;
|
||||
}
|
||||
//修正大小
|
||||
out->get()->linesize[0] = out->get()->nb_samples * out->get()->channels * av_get_bytes_per_sample((enum AVSampleFormat)out->get()->format);
|
||||
return out;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
void FFmpegFrame::fillPicture(AVPixelFormat target_format, int target_width, int target_height){
|
||||
assert(_data == nullptr);
|
||||
_data = new char[avpicture_get_size(target_format, target_width, target_height)];
|
||||
avpicture_fill((AVPicture *) _frame.get(), (uint8_t *) _data, target_format, target_width, target_height);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FFmpegDecoder::FFmpegDecoder(const Track::Ptr &track) {
|
||||
_frame_pool.setSize(8);
|
||||
avcodec_register_all();
|
||||
AVCodec *codec = nullptr;
|
||||
AVCodec *codec_default = nullptr;
|
||||
switch (track->getCodecId()) {
|
||||
case CodecH264:
|
||||
codec_default = getCodec(AV_CODEC_ID_H264);
|
||||
codec = getCodec(AV_CODEC_ID_H264, "h264_cuvid","h264_videotoolbox");
|
||||
break;
|
||||
case CodecH265:
|
||||
codec_default = getCodec(AV_CODEC_ID_HEVC);
|
||||
codec = getCodec(AV_CODEC_ID_HEVC, "hevc_cuvid","hevc_videotoolbox");
|
||||
break;
|
||||
case CodecAAC:
|
||||
codec = getCodec(AV_CODEC_ID_AAC);
|
||||
break;
|
||||
case CodecG711A:
|
||||
codec = getCodec(AV_CODEC_ID_PCM_ALAW);
|
||||
break;
|
||||
case CodecG711U:
|
||||
codec = getCodec(AV_CODEC_ID_PCM_MULAW);
|
||||
break;
|
||||
case CodecOpus:
|
||||
codec = getCodec(AV_CODEC_ID_OPUS);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (!codec) {
|
||||
throw std::runtime_error("未找到解码器");
|
||||
}
|
||||
|
||||
while (true) {
|
||||
_context.reset(avcodec_alloc_context3(codec), [](AVCodecContext *ctx) {
|
||||
avcodec_close(ctx);
|
||||
avcodec_free_context(&ctx);
|
||||
});
|
||||
|
||||
if (!_context) {
|
||||
throw std::runtime_error("创建解码器失败");
|
||||
}
|
||||
|
||||
//保存AVFrame的引用
|
||||
_context->refcounted_frames = 1;
|
||||
_context->flags |= AV_CODEC_FLAG_LOW_DELAY;
|
||||
_context->flags2 |= AV_CODEC_FLAG2_FAST;
|
||||
|
||||
switch (track->getCodecId()) {
|
||||
case CodecG711A:
|
||||
case CodecG711U: {
|
||||
AudioTrack::Ptr audio = static_pointer_cast<AudioTrack>(track);
|
||||
_context->channels = audio->getAudioChannel();
|
||||
_context->sample_rate = audio->getAudioSampleRate();
|
||||
_context->channel_layout = _context->channels == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
AVDictionary *dict = nullptr;
|
||||
av_dict_set(&dict, "threads", to_string(thread::hardware_concurrency()).data(), 0);
|
||||
av_dict_set(&dict, "zerolatency", "1", 0);
|
||||
av_dict_set(&dict, "strict", "-2", 0);
|
||||
|
||||
if (codec->capabilities & AV_CODEC_CAP_TRUNCATED) {
|
||||
/* we do not send complete frames */
|
||||
_context->flags |= AV_CODEC_FLAG_TRUNCATED;
|
||||
}
|
||||
|
||||
int ret = avcodec_open2(_context.get(), codec, &dict);
|
||||
if (ret >= 0) {
|
||||
//成功
|
||||
InfoL << "打开解码器成功:" << codec->name;
|
||||
break;
|
||||
}
|
||||
|
||||
if (codec_default && codec_default != codec) {
|
||||
//硬件编解码器打开失败,尝试软件的
|
||||
WarnL << "打开解码器" << codec->name << "失败,原因是:" << ffmpeg_err(ret) << ", 再尝试打开解码器" << codec_default->name;
|
||||
codec = codec_default;
|
||||
continue;
|
||||
}
|
||||
throw std::runtime_error(StrPrinter << "打开解码器" << codec->name << "失败:" << ffmpeg_err(ret));
|
||||
}
|
||||
}
|
||||
|
||||
void FFmpegDecoder::flush(){
|
||||
while (true) {
|
||||
FFmpegFrame::Ptr out_frame = _frame_pool.obtain();
|
||||
auto ret = avcodec_receive_frame(_context.get(), out_frame->get());
|
||||
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
||||
break;
|
||||
}
|
||||
if (ret < 0) {
|
||||
WarnL << "avcodec_receive_frame failed:" << ffmpeg_err(ret);
|
||||
break;
|
||||
}
|
||||
onDecode(out_frame);
|
||||
}
|
||||
}
|
||||
|
||||
const AVCodecContext *FFmpegDecoder::getContext() const{
|
||||
return _context.get();
|
||||
}
|
||||
|
||||
void FFmpegDecoder::inputFrame(const Frame::Ptr &frame) {
|
||||
inputFrame(frame->data(), frame->size(), frame->dts(), frame->pts());
|
||||
}
|
||||
|
||||
void FFmpegDecoder::inputFrame(const char *data, size_t size, uint32_t dts, uint32_t pts) {
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
|
||||
pkt.data = (uint8_t *) data;
|
||||
pkt.size = size;
|
||||
pkt.dts = dts;
|
||||
pkt.pts = pts;
|
||||
|
||||
auto ret = avcodec_send_packet(_context.get(), &pkt);
|
||||
if (ret < 0) {
|
||||
if (ret != AVERROR_INVALIDDATA) {
|
||||
WarnL << "avcodec_send_packet failed:" << ffmpeg_err(ret);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
FFmpegFrame::Ptr out_frame = _frame_pool.obtain();
|
||||
ret = avcodec_receive_frame(_context.get(), out_frame->get());
|
||||
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
|
||||
break;
|
||||
}
|
||||
if (ret < 0) {
|
||||
WarnL << "avcodec_receive_frame failed:" << ffmpeg_err(ret);
|
||||
break;
|
||||
}
|
||||
if (pts - out_frame->get()->pkt_pts > MAX_DELAY_SECOND * 1000 && _ticker.createdTime() > 10 * 1000) {
|
||||
//后面的帧才忽略,防止Track无法ready
|
||||
WarnL << "解码时,忽略" << MAX_DELAY_SECOND << "秒前的数据:" << pts << " " << out_frame->get()->pkt_pts;
|
||||
continue;
|
||||
}
|
||||
onDecode(out_frame);
|
||||
}
|
||||
}
|
||||
|
||||
void FFmpegDecoder::setOnDecode(FFmpegDecoder::onDec cb) {
|
||||
_cb = std::move(cb);
|
||||
}
|
||||
|
||||
void FFmpegDecoder::onDecode(const FFmpegFrame::Ptr &frame){
|
||||
if (_context->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
if (!_swr) {
|
||||
//固定输出16位整型的pcm
|
||||
_swr = std::make_shared<FFmpegSwr>(AV_SAMPLE_FMT_S16, frame->get()->channels, frame->get()->channel_layout, frame->get()->sample_rate);
|
||||
}
|
||||
//音频情况下,转换音频format类型,比如说浮点型转换为int型
|
||||
const_cast<FFmpegFrame::Ptr &>(frame) = _swr->inputFrame(frame);
|
||||
}
|
||||
if (_cb && frame) {
|
||||
_cb(frame);
|
||||
}
|
||||
}
|
||||
|
||||
92
player/FFMpegDecoder.h
Normal file
92
player/FFMpegDecoder.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
||||
*
|
||||
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
||||
*
|
||||
* Use of this source code is governed by MIT license that can be found in the
|
||||
* LICENSE file in the root of the source tree. All contributing project authors
|
||||
* may be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef FFMpegDecoder_H_
|
||||
#define FFMpegDecoder_H_
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include "Extension/Frame.h"
|
||||
#include "Extension/Track.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "libavcodec/avcodec.h"
|
||||
#include "libswresample/swresample.h"
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace mediakit;
|
||||
|
||||
class FFmpegFrame {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FFmpegFrame>;
|
||||
|
||||
FFmpegFrame(std::shared_ptr<AVFrame> frame = nullptr);
|
||||
~FFmpegFrame();
|
||||
|
||||
AVFrame *get() const;
|
||||
void fillPicture(AVPixelFormat target_format, int target_width, int target_height);
|
||||
|
||||
private:
|
||||
char *_data = nullptr;
|
||||
std::shared_ptr<AVFrame> _frame;
|
||||
};
|
||||
|
||||
class FFmpegSwr{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FFmpegSwr>;
|
||||
|
||||
FFmpegSwr(AVSampleFormat output, int channel, int channel_layout, int samplerate);
|
||||
~FFmpegSwr();
|
||||
|
||||
FFmpegFrame::Ptr inputFrame(const FFmpegFrame::Ptr &frame);
|
||||
|
||||
private:
|
||||
int _target_channels;
|
||||
int _target_channel_layout;
|
||||
int _target_samplerate;
|
||||
AVSampleFormat _target_format;
|
||||
SwrContext *_ctx = nullptr;
|
||||
ResourcePool<FFmpegFrame> _frame_pool;
|
||||
};
|
||||
|
||||
class FFmpegDecoder : public FrameWriterInterface {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<FFmpegDecoder>;
|
||||
using onDec = function<void(const FFmpegFrame::Ptr &)>;
|
||||
|
||||
FFmpegDecoder(const Track::Ptr &track);
|
||||
~FFmpegDecoder() {}
|
||||
|
||||
void inputFrame(const Frame::Ptr &frame) override;
|
||||
void inputFrame(const char *data, size_t size, uint32_t dts, uint32_t pts);
|
||||
|
||||
void setOnDecode(onDec cb);
|
||||
void flush();
|
||||
const AVCodecContext *getContext() const;
|
||||
|
||||
private:
|
||||
void onDecode(const FFmpegFrame::Ptr &frame);
|
||||
|
||||
private:
|
||||
Ticker _ticker;
|
||||
onDec _cb;
|
||||
FFmpegSwr::Ptr _swr;
|
||||
ResourcePool<FFmpegFrame> _frame_pool;
|
||||
std::shared_ptr<AVCodecContext> _context;
|
||||
};
|
||||
|
||||
#endif /* FFMpegDecoder_H_ */
|
||||
|
||||
|
||||
93
player/SDLAudioDevice.cpp
Normal file
93
player/SDLAudioDevice.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
||||
*
|
||||
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
||||
*
|
||||
* Use of this source code is governed by MIT license that can be found in the
|
||||
* LICENSE file in the root of the source tree. All contributing project authors
|
||||
* may be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "Util/logger.h"
|
||||
#include "AudioSRC.h"
|
||||
#include "SDLAudioDevice.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace toolkit;
|
||||
|
||||
INSTANCE_IMP(SDLAudioDevice);
|
||||
|
||||
SDLAudioDevice::~SDLAudioDevice() {
|
||||
SDL_CloseAudio();
|
||||
}
|
||||
|
||||
SDLAudioDevice::SDLAudioDevice() {
|
||||
SDL_AudioSpec wanted_spec;
|
||||
wanted_spec.freq = DEFAULT_SAMPLERATE;
|
||||
wanted_spec.format = DEFAULT_FORMAT;
|
||||
wanted_spec.channels = DEFAULT_CHANNEL;
|
||||
wanted_spec.silence = 0;
|
||||
wanted_spec.samples = DEFAULT_SAMPLES;
|
||||
wanted_spec.userdata = this;
|
||||
wanted_spec.callback = [](void *userdata, Uint8 *stream, int len) {
|
||||
SDLAudioDevice *_this = (SDLAudioDevice *) userdata;
|
||||
_this->onReqPCM((char *) stream, len);
|
||||
};
|
||||
if (SDL_OpenAudio(&wanted_spec, &_audio_config) < 0) {
|
||||
throw std::runtime_error("SDL_OpenAudio failed");
|
||||
}
|
||||
|
||||
InfoL << "actual audioSpec, " << "freq:" << _audio_config.freq
|
||||
<< ", format:" << hex << _audio_config.format << dec
|
||||
<< ", channels:" << (int) _audio_config.channels
|
||||
<< ", samples:" << _audio_config.samples
|
||||
<< ", pcm size:" << _audio_config.size;
|
||||
|
||||
_play_buf.reset(new char[_audio_config.size], [](char *ptr) {
|
||||
delete[] ptr;
|
||||
});
|
||||
}
|
||||
|
||||
void SDLAudioDevice::addChannel(AudioSRC *chn) {
|
||||
lock_guard<recursive_mutex> lck(_channel_mtx);
|
||||
if (_channels.empty()) {
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
chn->setOutputAudioConfig(_audio_config);
|
||||
_channels.emplace(chn);
|
||||
}
|
||||
|
||||
void SDLAudioDevice::delChannel(AudioSRC *chn) {
|
||||
lock_guard<recursive_mutex> lck(_channel_mtx);
|
||||
_channels.erase(chn);
|
||||
if (_channels.empty()) {
|
||||
SDL_PauseAudio(true);
|
||||
}
|
||||
}
|
||||
|
||||
void SDLAudioDevice::onReqPCM(char *stream, int len) {
|
||||
lock_guard<recursive_mutex> lck(_channel_mtx);
|
||||
int size;
|
||||
int channel = 0;
|
||||
for (auto &chn : _channels) {
|
||||
if (channel == 0) {
|
||||
size = chn->getPCMData(_play_buf.get(), len);
|
||||
if (size) {
|
||||
memcpy(stream, _play_buf.get(), size);
|
||||
}
|
||||
} else {
|
||||
size = chn->getPCMData(_play_buf.get(), len);
|
||||
if (size) {
|
||||
SDL_MixAudio((Uint8 *) stream, (Uint8 *) _play_buf.get(), size, SDL_MIX_MAXVOLUME);
|
||||
}
|
||||
}
|
||||
if (size) {
|
||||
channel++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!channel) {
|
||||
memset(stream, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
50
player/SDLAudioDevice.h
Normal file
50
player/SDLAudioDevice.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
||||
*
|
||||
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
||||
*
|
||||
* Use of this source code is governed by MIT license that can be found in the
|
||||
* LICENSE file in the root of the source tree. All contributing project authors
|
||||
* may be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef SDLAUDIOMIXER_SDLAUDIODEVICE_H_
|
||||
#define SDLAUDIOMIXER_SDLAUDIODEVICE_H_
|
||||
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <unordered_set>
|
||||
|
||||
#define DEFAULT_SAMPLERATE 48000
|
||||
#define DEFAULT_FORMAT AUDIO_S16
|
||||
#define DEFAULT_CHANNEL 2
|
||||
#define DEFAULT_SAMPLES 1024
|
||||
|
||||
using namespace std;
|
||||
|
||||
class AudioSRC;
|
||||
|
||||
//该对象主要实现sdl混音与播放
|
||||
class SDLAudioDevice : public std::enable_shared_from_this<SDLAudioDevice>{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<SDLAudioDevice>;
|
||||
|
||||
~SDLAudioDevice();
|
||||
static SDLAudioDevice &Instance();
|
||||
|
||||
void addChannel(AudioSRC *chn);
|
||||
void delChannel(AudioSRC *chn);
|
||||
|
||||
private:
|
||||
SDLAudioDevice();
|
||||
void onReqPCM(char *stream, int len);
|
||||
|
||||
private:
|
||||
std::shared_ptr<char> _play_buf;
|
||||
SDL_AudioSpec _audio_config;
|
||||
recursive_mutex _channel_mtx;
|
||||
unordered_set<AudioSRC *> _channels;
|
||||
};
|
||||
|
||||
#endif /* SDLAUDIOMIXER_SDLAUDIODEVICE_H_ */
|
||||
193
player/YuvDisplayer.h
Normal file
193
player/YuvDisplayer.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
||||
*
|
||||
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
||||
*
|
||||
* Use of this source code is governed by MIT license that can be found in the
|
||||
* LICENSE file in the root of the source tree. All contributing project authors
|
||||
* may be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef YUVDISPLAYER_H_
|
||||
#define YUVDISPLAYER_H_
|
||||
#include <stdexcept>
|
||||
#include "Util/onceToken.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "SDL2/SDL.h"
|
||||
#include "libavcodec/avcodec.h"
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#pragma comment(lib,"SDL2.lib")
|
||||
#endif //defined(_WIN32)
|
||||
|
||||
using namespace toolkit;
|
||||
using namespace mediakit;
|
||||
|
||||
#define REFRESH_EVENT (SDL_USEREVENT + 1)
|
||||
|
||||
|
||||
class SDLDisplayerHelper
|
||||
{
|
||||
public:
|
||||
static SDLDisplayerHelper &Instance(){
|
||||
static SDLDisplayerHelper *instance(new SDLDisplayerHelper);
|
||||
return *instance;
|
||||
}
|
||||
static void Destory(){
|
||||
delete &Instance();
|
||||
}
|
||||
template<typename FUN>
|
||||
void doTask(FUN &&f){
|
||||
{
|
||||
lock_guard<mutex> lck(_mtxTask);
|
||||
_taskList.emplace_back(f);
|
||||
}
|
||||
SDL_Event event;
|
||||
event.type = REFRESH_EVENT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void runLoop(){
|
||||
bool flag = true;
|
||||
std::function<bool ()> task;
|
||||
SDL_Event event;
|
||||
while(flag){
|
||||
SDL_WaitEvent(&event);
|
||||
switch (event.type){
|
||||
case REFRESH_EVENT:{
|
||||
{
|
||||
lock_guard<mutex> lck(_mtxTask);
|
||||
if(_taskList.empty()){
|
||||
//not reachable
|
||||
continue;
|
||||
}
|
||||
task = _taskList.front();
|
||||
_taskList.pop_front();
|
||||
}
|
||||
flag = task();
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
InfoL << event.type;
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown(){
|
||||
doTask([](){return false;});
|
||||
}
|
||||
private:
|
||||
SDLDisplayerHelper(){
|
||||
};
|
||||
~SDLDisplayerHelper(){
|
||||
shutdown();
|
||||
};
|
||||
private:
|
||||
std::deque<std::function<bool ()> > _taskList;
|
||||
std::mutex _mtxTask;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class YuvDisplayer {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<YuvDisplayer>;
|
||||
|
||||
YuvDisplayer(void *hwnd = nullptr,const char *title = "untitled"){
|
||||
static onceToken token([]() {
|
||||
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) == -1) {
|
||||
string err = "初始化SDL失败:";
|
||||
err+= SDL_GetError();
|
||||
ErrorL << err;
|
||||
throw std::runtime_error(err);
|
||||
}
|
||||
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_CRITICAL);
|
||||
SDL_LogSetOutputFunction([](void *userdata, int category, SDL_LogPriority priority, const char *message){
|
||||
DebugL << category << " " << priority << message;
|
||||
},nullptr);
|
||||
InfoL << "SDL_Init";
|
||||
}, []() {
|
||||
SDLDisplayerHelper::Destory();
|
||||
SDL_Quit();
|
||||
});
|
||||
|
||||
_title = title;
|
||||
_hwnd = hwnd;
|
||||
}
|
||||
virtual ~YuvDisplayer(){
|
||||
if (_texture) {
|
||||
SDL_DestroyTexture(_texture);
|
||||
_texture = nullptr;
|
||||
}
|
||||
if (_render) {
|
||||
SDL_DestroyRenderer(_render);
|
||||
_render = nullptr;
|
||||
}
|
||||
if (_win) {
|
||||
SDL_DestroyWindow(_win);
|
||||
_win = nullptr;
|
||||
}
|
||||
}
|
||||
bool displayYUV(AVFrame *pFrame){
|
||||
if (!_win) {
|
||||
if (_hwnd) {
|
||||
_win = SDL_CreateWindowFrom(_hwnd);
|
||||
}else {
|
||||
_win = SDL_CreateWindow(_title.data(),
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
pFrame->width,
|
||||
pFrame->height,
|
||||
SDL_WINDOW_OPENGL);
|
||||
}
|
||||
}
|
||||
if (_win && ! _render){
|
||||
#if 0
|
||||
SDL_RENDERER_SOFTWARE = 0x00000001, /**< The renderer is a software fallback */
|
||||
SDL_RENDERER_ACCELERATED = 0x00000002, /**< The renderer uses hardware
|
||||
acceleration */
|
||||
SDL_RENDERER_PRESENTVSYNC = 0x00000004, /**< Present is synchronized
|
||||
with the refresh rate */
|
||||
SDL_RENDERER_TARGETTEXTURE = 0x00000008 /**< The renderer supports
|
||||
rendering to texture */
|
||||
#endif
|
||||
|
||||
_render = SDL_CreateRenderer(_win, -1, SDL_RENDERER_ACCELERATED);
|
||||
}
|
||||
if (_render && !_texture) {
|
||||
_texture = SDL_CreateTexture(_render, SDL_PIXELFORMAT_IYUV,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
pFrame->width,
|
||||
pFrame->height);
|
||||
}
|
||||
if (_texture) {
|
||||
SDL_UpdateYUVTexture(_texture, nullptr,
|
||||
pFrame->data[0], pFrame->linesize[0],
|
||||
pFrame->data[1], pFrame->linesize[1],
|
||||
pFrame->data[2], pFrame->linesize[2]);
|
||||
|
||||
//SDL_UpdateTexture(_texture, nullptr, pFrame->data[0], pFrame->linesize[0]);
|
||||
SDL_RenderClear(_render);
|
||||
SDL_RenderCopy(_render, _texture, nullptr, nullptr);
|
||||
SDL_RenderPresent(_render);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
string _title;
|
||||
SDL_Window *_win = nullptr;
|
||||
SDL_Renderer *_render = nullptr;
|
||||
SDL_Texture *_texture = nullptr;
|
||||
void *_hwnd = nullptr;
|
||||
};
|
||||
|
||||
#endif /* YUVDISPLAYER_H_ */
|
||||
115
player/test_player.cpp
Normal file
115
player/test_player.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
||||
*
|
||||
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
||||
*
|
||||
* Use of this source code is governed by MIT license that can be found in the
|
||||
* LICENSE file in the root of the source tree. All contributing project authors
|
||||
* may be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include "Util/logger.h"
|
||||
#include <iostream>
|
||||
#include "Rtsp/UDPServer.h"
|
||||
#include "Player/MediaPlayer.h"
|
||||
#include "Util/onceToken.h"
|
||||
#include "FFMpegDecoder.h"
|
||||
#include "YuvDisplayer.h"
|
||||
#include "AudioSRC.h"
|
||||
using namespace std;
|
||||
using namespace toolkit;
|
||||
using namespace mediakit;
|
||||
|
||||
#ifdef WIN32
|
||||
#include <TCHAR.h>
|
||||
|
||||
extern int __argc;
|
||||
extern TCHAR** __targv;
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstanc, LPSTR lpCmdLine, int nShowCmd) {
|
||||
int argc = __argc;
|
||||
char **argv = __targv;
|
||||
|
||||
//1. 首先调用AllocConsole创建一个控制台窗口
|
||||
AllocConsole();
|
||||
|
||||
//2. 但此时调用cout或者printf都不能正常输出文字到窗口(包括输入流cin和scanf), 所以需要如下重定向输入输出流:
|
||||
FILE* stream;
|
||||
freopen_s(&stream, "CON", "r", stdin);//重定向输入流
|
||||
freopen_s(&stream, "CON", "w", stdout);//重定向输入流
|
||||
|
||||
//3. 如果我们需要用到控制台窗口句柄,可以调用FindWindow取得:
|
||||
HWND _consoleHwnd;
|
||||
SetConsoleTitleA("test_player");//设置窗口名
|
||||
#else
|
||||
#include <unistd.h>
|
||||
int main(int argc, char *argv[]) {
|
||||
#endif
|
||||
static char *url = argv[1];
|
||||
//设置退出信号处理函数
|
||||
signal(SIGINT, [](int) { SDLDisplayerHelper::Instance().shutdown(); });
|
||||
//设置日志
|
||||
Logger::Instance().add(std::make_shared<ConsoleChannel>());
|
||||
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
||||
|
||||
if (argc != 3) {
|
||||
ErrorL << "\r\n测试方法:./test_player rtxp_url rtp_type\r\n"
|
||||
<< "例如:./test_player rtsp://admin:123456@127.0.0.1/live/0 0\r\n"
|
||||
<< endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto player = std::make_shared<MediaPlayer>();
|
||||
//sdl要求在main线程初始化
|
||||
auto displayer = std::make_shared<YuvDisplayer>(nullptr, url);
|
||||
weak_ptr<MediaPlayer> weakPlayer = player;
|
||||
player->setOnPlayResult([weakPlayer, displayer](const SockException &ex) {
|
||||
InfoL << "OnPlayResult:" << ex.what();
|
||||
auto strongPlayer = weakPlayer.lock();
|
||||
if (ex || !strongPlayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto videoTrack = dynamic_pointer_cast<VideoTrack>(strongPlayer->getTrack(TrackVideo, false));
|
||||
auto audioTrack = dynamic_pointer_cast<AudioTrack>(strongPlayer->getTrack(TrackAudio,false));
|
||||
|
||||
if (videoTrack) {
|
||||
auto decoder = std::make_shared<FFmpegDecoder>(videoTrack);
|
||||
decoder->setOnDecode([displayer](const FFmpegFrame::Ptr &yuv) {
|
||||
SDLDisplayerHelper::Instance().doTask([yuv, displayer]() {
|
||||
//sdl要求在main线程渲染
|
||||
displayer->displayYUV(yuv->get());
|
||||
return true;
|
||||
});
|
||||
});
|
||||
auto merger = std::make_shared<FrameMerger>(FrameMerger::h264_prefix);
|
||||
auto delegate = std::make_shared<FrameWriterInterfaceHelper>([decoder, merger](const Frame::Ptr &frame) {
|
||||
merger->inputFrame(frame, [&](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer, bool have_idr) {
|
||||
decoder->inputFrame(buffer->data(), buffer->size(), dts, pts);
|
||||
});
|
||||
});
|
||||
videoTrack->addDelegate(delegate);
|
||||
}
|
||||
|
||||
if (audioTrack) {
|
||||
auto decoder = std::make_shared<FFmpegDecoder>(audioTrack);
|
||||
auto audio_player = std::make_shared<AudioPlayer>();
|
||||
//FFmpeg解码时已经统一转换为16位整型pcm
|
||||
audio_player->setup(audioTrack->getAudioSampleRate(), audioTrack->getAudioChannel(), AUDIO_S16);
|
||||
decoder->setOnDecode([audio_player](const FFmpegFrame::Ptr &pcm) {
|
||||
audio_player->inputFrame((const char *) (pcm->get()->data[0]), pcm->get()->linesize[0]);
|
||||
});
|
||||
auto audio_delegate = std::make_shared<FrameWriterInterfaceHelper>( [decoder](const Frame::Ptr &frame) {
|
||||
decoder->inputFrame(frame);
|
||||
});
|
||||
audioTrack->addDelegate(audio_delegate);
|
||||
}
|
||||
});
|
||||
|
||||
(*player)[kRtpType] = atoi(argv[2]);
|
||||
player->play(argv[1]);
|
||||
SDLDisplayerHelper::Instance().runLoop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user