| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- #include "record_audio_dshow.h"
- #include "headers_ffmpeg.h"
- #include "error_define.h"
- #include "log_helper.h"
- namespace am {
- record_audio_dshow::record_audio_dshow()
- {
- ffmpeg_register_all();
- ffmpeg_register_devices();
- _fmt_ctx = NULL;
- _input_fmt = NULL;
- _codec_ctx = NULL;
- _codec = NULL;
- _stream_index = -1;
- }
- record_audio_dshow::~record_audio_dshow()
- {
- stop();
- cleanup();
- }
- int record_audio_dshow::init(const std::string &device_name,
- const std::string &device_id,
- bool is_input)
- {
- int error = AE_NO;
- int ret = 0;
- if (_inited == true)
- return error;
- do {
- _device_name = device_name;
- _device_id = device_id;
- _is_input = is_input;
- _input_fmt = av_find_input_format("dshow");
- if (!_input_fmt) {
- error = AE_FFMPEG_FIND_INPUT_FMT_FAILED;
- break;
- }
- _fmt_ctx = avformat_alloc_context();
- ret = avformat_open_input(&_fmt_ctx, _device_name.c_str(), _input_fmt, NULL);
- if (ret != 0) {
- error = AE_FFMPEG_OPEN_INPUT_FAILED;
- break;
- }
- ret = avformat_find_stream_info(_fmt_ctx, NULL);
- if (ret < 0) {
- error = AE_FFMPEG_FIND_STREAM_FAILED;
- break;
- }
- int stream_index = -1;
- for (int i = 0; i < _fmt_ctx->nb_streams; i++) {
- if (ffmpeg_get_codec_type(_fmt_ctx->streams[i]) == AVMEDIA_TYPE_AUDIO) {
- stream_index = i;
- break;
- }
- }
- if (stream_index == -1) {
- error = AE_FFMPEG_FIND_STREAM_FAILED;
- break;
- }
- _stream_index = stream_index;
- _codec_ctx = ffmpeg_get_codec_context(_fmt_ctx->streams[stream_index]);
- _codec = (AVCodec*)avcodec_find_decoder(_codec_ctx->codec_id);
- if (_codec == NULL) {
- error = AE_FFMPEG_FIND_DECODER_FAILED;
- break;
- }
- ret = avcodec_open2(_codec_ctx, _codec, NULL);
- if (ret != 0) {
- error = AE_FFMPEG_OPEN_CODEC_FAILED;
- break;
- }
- _inited = true;
- _sample_rate = _codec_ctx->sample_rate;
- _bit_rate = _codec_ctx->bit_rate;
- _bit_per_sample = _codec_ctx->bits_per_coded_sample;
- _channel_num = ffmpeg_get_channels(_codec_ctx);
- _fmt = _codec_ctx->sample_fmt;
- _inited = true;
- } while (0);
- if (error != AE_NO) {
- al_debug("%s,error:%d", err2str(error), ret);
- cleanup();
- }
- return error;
- }
- int record_audio_dshow::start()
- {
- if (_running == true) {
- al_warn("record audio dshow is already running");
- return AE_NO;
- }
- if (_inited == false) {
- return AE_NEED_INIT;
- }
- _running = true;
- _thread = std::thread(std::bind(&record_audio_dshow::record_loop, this));
- return AE_NO;
- }
- int record_audio_dshow::pause()
- {
- return 0;
- }
- int record_audio_dshow::resume()
- {
- return 0;
- }
- int record_audio_dshow::stop()
- {
- _running = false;
- if (_thread.joinable())
- _thread.join();
- return AE_NO;
- }
- const AVRational record_audio_dshow::get_time_base()
- {
- if (_inited && _fmt_ctx && _stream_index != -1) {
- return _fmt_ctx->streams[_stream_index]->time_base;
- } else {
- return {1, AV_TIME_BASE};
- }
- }
- int64_t record_audio_dshow::get_start_time()
- {
- return _fmt_ctx->streams[_stream_index]->start_time;
- }
- int record_audio_dshow::decode(AVFrame *frame, AVPacket *packet)
- {
- int ret = avcodec_send_packet(_codec_ctx, packet);
- if (ret < 0) {
- al_error("avcodec_send_packet failed:%d", ret);
- return AE_FFMPEG_DECODE_FRAME_FAILED;
- }
- while (ret >= 0) {
- ret = avcodec_receive_frame(_codec_ctx, frame);
- if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
- break;
- }
- if (ret < 0) {
- return AE_FFMPEG_READ_FRAME_FAILED;
- }
- if (ret == 0 && _on_data)
- _on_data(frame, _cb_extra_index);
- av_frame_unref(
- frame); //need to do this? avcodec_receive_frame said will call unref before receive
- }
- return AE_NO;
- }
- void record_audio_dshow::record_loop()
- {
- int ret = 0;
- AVPacket *packet = av_packet_alloc();
- AVFrame *frame = av_frame_alloc();
- while (_running == true) {
- av_init_packet(packet);
- ret = av_read_frame(_fmt_ctx, packet);
- if (ret < 0) {
- if (_on_error)
- _on_error(AE_FFMPEG_READ_FRAME_FAILED, _cb_extra_index);
- break;
- }
- if (packet->stream_index == _stream_index) {
- ret = decode(frame, packet);
- if (ret != AE_NO) {
- if (_on_error)
- _on_error(AE_FFMPEG_DECODE_FRAME_FAILED, _cb_extra_index);
- al_fatal("decode pcm packet failed:%d", ret);
- break;
- }
- }
- av_packet_unref(packet);
- }
- //flush packet left in decoder
- decode(frame, NULL);
- av_packet_free(&packet);
- av_frame_free(&frame);
- }
- void record_audio_dshow::cleanup()
- {
- if (_codec_ctx)
- avcodec_close(_codec_ctx);
- if (_fmt_ctx)
- avformat_close_input(&_fmt_ctx);
- _fmt_ctx = NULL;
- _input_fmt = NULL;
- _codec_ctx = NULL;
- _codec = NULL;
- _stream_index = -1;
- _inited = false;
- }
- } // namespace am
|