record_audio_dshow.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #include "record_audio_dshow.h"
  2. #include "error_define.h"
  3. #include "log_helper.h"
  4. namespace am {
  5. record_audio_dshow::record_audio_dshow()
  6. {
  7. av_register_all();
  8. avdevice_register_all();
  9. _fmt_ctx = NULL;
  10. _input_fmt = NULL;
  11. _codec_ctx = NULL;
  12. _codec = NULL;
  13. _stream_index = -1;
  14. }
  15. record_audio_dshow::~record_audio_dshow()
  16. {
  17. stop();
  18. cleanup();
  19. }
  20. int record_audio_dshow::init(const std::string &device_name,
  21. const std::string &device_id,
  22. bool is_input)
  23. {
  24. int error = AE_NO;
  25. int ret = 0;
  26. if (_inited == true)
  27. return error;
  28. do {
  29. _device_name = device_name;
  30. _device_id = device_id;
  31. _is_input = is_input;
  32. _input_fmt = av_find_input_format("dshow");
  33. if (!_input_fmt) {
  34. error = AE_FFMPEG_FIND_INPUT_FMT_FAILED;
  35. break;
  36. }
  37. _fmt_ctx = avformat_alloc_context();
  38. ret = avformat_open_input(&_fmt_ctx, _device_name.c_str(), _input_fmt, NULL);
  39. if (ret != 0) {
  40. error = AE_FFMPEG_OPEN_INPUT_FAILED;
  41. break;
  42. }
  43. ret = avformat_find_stream_info(_fmt_ctx, NULL);
  44. if (ret < 0) {
  45. error = AE_FFMPEG_FIND_STREAM_FAILED;
  46. break;
  47. }
  48. int stream_index = -1;
  49. for (int i = 0; i < _fmt_ctx->nb_streams; i++) {
  50. if (_fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
  51. stream_index = i;
  52. break;
  53. }
  54. }
  55. if (stream_index == -1) {
  56. error = AE_FFMPEG_FIND_STREAM_FAILED;
  57. break;
  58. }
  59. _stream_index = stream_index;
  60. _codec_ctx = _fmt_ctx->streams[stream_index]->codec;
  61. _codec = avcodec_find_decoder(_codec_ctx->codec_id);
  62. if (_codec == NULL) {
  63. error = AE_FFMPEG_FIND_DECODER_FAILED;
  64. break;
  65. }
  66. ret = avcodec_open2(_codec_ctx, _codec, NULL);
  67. if (ret != 0) {
  68. error = AE_FFMPEG_OPEN_CODEC_FAILED;
  69. break;
  70. }
  71. _inited = true;
  72. _sample_rate = _codec_ctx->sample_rate;
  73. _bit_rate = _codec_ctx->bit_rate;
  74. _bit_per_sample = _codec_ctx->bits_per_coded_sample;
  75. _channel_num = _codec_ctx->channels;
  76. _fmt = _codec_ctx->sample_fmt;
  77. _inited = true;
  78. } while (0);
  79. if (error != AE_NO) {
  80. al_debug("%s,error:%d", err2str(error), ret);
  81. cleanup();
  82. }
  83. return error;
  84. }
  85. int record_audio_dshow::start()
  86. {
  87. if (_running == true) {
  88. al_warn("record audio dshow is already running");
  89. return AE_NO;
  90. }
  91. if (_inited == false) {
  92. return AE_NEED_INIT;
  93. }
  94. _running = true;
  95. _thread = std::thread(std::bind(&record_audio_dshow::record_loop, this));
  96. return AE_NO;
  97. }
  98. int record_audio_dshow::pause()
  99. {
  100. return 0;
  101. }
  102. int record_audio_dshow::resume()
  103. {
  104. return 0;
  105. }
  106. int record_audio_dshow::stop()
  107. {
  108. _running = false;
  109. if (_thread.joinable())
  110. _thread.join();
  111. return AE_NO;
  112. }
  113. const AVRational record_audio_dshow::get_time_base()
  114. {
  115. if (_inited && _fmt_ctx && _stream_index != -1) {
  116. return _fmt_ctx->streams[_stream_index]->time_base;
  117. } else {
  118. return {1, AV_TIME_BASE};
  119. }
  120. }
  121. int64_t record_audio_dshow::get_start_time()
  122. {
  123. return _fmt_ctx->streams[_stream_index]->start_time;
  124. }
  125. int record_audio_dshow::decode(AVFrame *frame, AVPacket *packet)
  126. {
  127. int ret = avcodec_send_packet(_codec_ctx, packet);
  128. if (ret < 0) {
  129. al_error("avcodec_send_packet failed:%d", ret);
  130. return AE_FFMPEG_DECODE_FRAME_FAILED;
  131. }
  132. while (ret >= 0) {
  133. ret = avcodec_receive_frame(_codec_ctx, frame);
  134. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
  135. break;
  136. }
  137. if (ret < 0) {
  138. return AE_FFMPEG_READ_FRAME_FAILED;
  139. }
  140. if (ret == 0 && _on_data)
  141. _on_data(frame, _cb_extra_index);
  142. av_frame_unref(
  143. frame); //need to do this? avcodec_receive_frame said will call unref before receive
  144. }
  145. return AE_NO;
  146. }
  147. void record_audio_dshow::record_loop()
  148. {
  149. int ret = 0;
  150. AVPacket *packet = av_packet_alloc();
  151. AVFrame *frame = av_frame_alloc();
  152. while (_running == true) {
  153. av_init_packet(packet);
  154. ret = av_read_frame(_fmt_ctx, packet);
  155. if (ret < 0) {
  156. if (_on_error)
  157. _on_error(AE_FFMPEG_READ_FRAME_FAILED, _cb_extra_index);
  158. break;
  159. }
  160. if (packet->stream_index == _stream_index) {
  161. ret = decode(frame, packet);
  162. if (ret != AE_NO) {
  163. if (_on_error)
  164. _on_error(AE_FFMPEG_DECODE_FRAME_FAILED, _cb_extra_index);
  165. al_fatal("decode pcm packet failed:%d", ret);
  166. break;
  167. }
  168. }
  169. av_packet_unref(packet);
  170. }
  171. //flush packet left in decoder
  172. decode(frame, NULL);
  173. av_packet_free(&packet);
  174. av_frame_free(&frame);
  175. }
  176. void record_audio_dshow::cleanup()
  177. {
  178. if (_codec_ctx)
  179. avcodec_close(_codec_ctx);
  180. if (_fmt_ctx)
  181. avformat_close_input(&_fmt_ctx);
  182. _fmt_ctx = NULL;
  183. _input_fmt = NULL;
  184. _codec_ctx = NULL;
  185. _codec = NULL;
  186. _stream_index = -1;
  187. _inited = false;
  188. }
  189. } // namespace am