c++
复制代码
extern "C" {
#include <libavcodec/bsf.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
#include <glog/logging.h>
#include <cstdio>
#include <string>
#include <fstream>
// Thread-local buffer to store FFmpeg error string
thread_local static char error_buffer[AV_ERROR_MAX_STRING_SIZE] = {};
/**
* @brief Convert FFmpeg error code to error string
* @param error_code FFmpeg error code
* @return error string
*/
static char *ErrorToString(const int error_code) {
std::memset(error_buffer, 0, AV_ERROR_MAX_STRING_SIZE);
return av_make_error_string(error_buffer, AV_ERROR_MAX_STRING_SIZE, error_code);
}
/**
* @brief Extract video stream from input file and save it to output file in Annex B format
* @param fmt_ctx AVFormatContext
* @param pkt AVPacket
* @param ofs Output file stream
* @return true on success, false on failure
*/
static bool InnerExtractVideoStreamAnnexB(AVFormatContext *fmt_ctx, AVPacket *pkt, std::ofstream &ofs) {
if (!fmt_ctx || !pkt || !ofs) {
return false;
}
int error_code{};
int h264_stream_index = -1;
// find h264 video stream
AVStream *stream = nullptr;
AVCodecParameters *codec_params = nullptr;
for (int i = 0; i < fmt_ctx->nb_streams; i++) {
stream = fmt_ctx->streams[i];
codec_params = stream->codecpar;
if (codec_params->codec_type == AVMEDIA_TYPE_VIDEO) {
if (codec_params->codec_id == AV_CODEC_ID_H264) {
h264_stream_index = i;
break;
}
}
}
if (h264_stream_index < 0) {
LOG(ERROR) << "Could not find H264 video stream";
return false;
}
// allocate bitstream filter context
const AVBitStreamFilter *bs_filter = av_bsf_get_by_name("h264_mp4toannexb");
if (bs_filter == nullptr) {
LOG(ERROR) << "Could not find h264_mp4toannexb bitstream filter";
return false;
}
AVBSFContext *bsf_ctx = nullptr;
if ((error_code = av_bsf_alloc(bs_filter, &bsf_ctx)) < 0) {
LOG(ERROR) << "Could not allocate bitstream filter context: " << ErrorToString(error_code);
return false;
}
// initialize bitstream filter context
if ((error_code = avcodec_parameters_copy(bsf_ctx->par_in, codec_params)) < 0) {
LOG(ERROR) << "Could not copy codec parameters: " << ErrorToString(error_code);
av_bsf_free(&bsf_ctx);
return false;
}
if ((error_code = av_bsf_init(bsf_ctx)) < 0) {
LOG(ERROR) << "Could not initialize bitstream filter context: " << ErrorToString(error_code);
av_bsf_free(&bsf_ctx);
return false;
}
while (true) {
if ((error_code = av_read_frame(fmt_ctx, pkt)) < 0) {
if (error_code != AVERROR_EOF) {
LOG(ERROR) << "Could not read frame: " << ErrorToString(error_code);
} else {
LOG(INFO) << "End of input file";
}
break;
}
if (pkt->stream_index != h264_stream_index) {
av_packet_unref(pkt);
continue;
}
// send packet to bitstream filter
if ((error_code = av_bsf_send_packet(bsf_ctx, pkt)) < 0) {
LOG(ERROR) << "Could not send packet to bitstream filter: " << ErrorToString(error_code);
av_packet_unref(pkt);
continue;
}
av_packet_unref(pkt);
// receive packet from bitstream filter
while ((error_code = av_bsf_receive_packet(bsf_ctx, pkt)) == 0) {
if (!ofs.write(reinterpret_cast<const char *>(pkt->data), pkt->size)) {
LOG(ERROR) << "Could not write ha64 file: ofstream is broken";
av_packet_unref(pkt);
return false;
}
LOG(INFO) << "Extracted " << pkt->size << " bytes of H264 data";
av_packet_unref(pkt);
}
if (error_code != AVERROR(EAGAIN)) {
av_packet_unref(pkt);
LOG(ERROR) << "Could not receive packet from bitstream filter: " << ErrorToString(error_code);
}
}
av_bsf_free(&bsf_ctx);
return true;
}
/**
* @brief Extract video stream from input file and save it to output file in Annex B format
* @param input_file Input file path
* @param output_file Output file path
*/
void ExtractVideoStreamAnnexB(const std::string &input_file, const std::string &output_file) {
int error_code{};
// open input_file
AVFormatContext *fmt_ctx = nullptr;
if ((error_code = avformat_open_input(&fmt_ctx, input_file.c_str(), nullptr, nullptr)) < 0) {
LOG(ERROR) << "Could not open source file \"" << input_file << "\": " << ErrorToString(error_code);
return;
}
if ((error_code = avformat_find_stream_info(fmt_ctx, nullptr)) < 0) {
LOG(ERROR) << "Could not find stream information: " << ErrorToString(error_code);
avformat_close_input(&fmt_ctx);
return;
}
av_dump_format(fmt_ctx, 0, input_file.c_str(), 0);
// open output_file
std::ofstream ofs(output_file, std::ios::out | std::ios::binary);
if (!ofs.is_open()) {
LOG(ERROR) << "Could not open output file \"" << output_file << "\"";
avformat_close_input(&fmt_ctx);
return;
}
// allocate AVPacket
AVPacket *pkt = nullptr;
if ((pkt = av_packet_alloc()) == nullptr) {
LOG(ERROR) << "Could not allocate AVPacket: av_packet_alloc()";
avformat_close_input(&fmt_ctx);
return;
}
InnerExtractVideoStreamAnnexB(fmt_ctx, pkt, ofs);
ofs.close();
av_packet_free(&pkt);
avformat_close_input(&fmt_ctx);
}
#if 0
int main(int argc, char *argv[]) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = true;
FLAGS_minloglevel = google::GLOG_INFO;
ExtractVideoStreamAnnexB("input.mp4", "output.h264");
google::ShutdownGoogleLogging();
return 0;
}
#endif