// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/filters/ffmpeg_h264_bitstream_converter.h"

#include "base/logging.h"
#include "media/ffmpeg/ffmpeg_common.h"

namespace media {

FFmpegH264BitstreamConverter::FFmpegH264BitstreamConverter(
    AVCodecContext* stream_context)
    : configuration_processed_(false),
      stream_context_(stream_context) {
  CHECK(stream_context_);
}

FFmpegH264BitstreamConverter::~FFmpegH264BitstreamConverter() {}

bool FFmpegH264BitstreamConverter::Initialize() {
  return true;
}

bool FFmpegH264BitstreamConverter::ConvertPacket(AVPacket* packet) {
  uint32 output_packet_size = 0;
  uint32 configuration_size = 0;
  uint32 io_size = 0;
  if (packet == NULL) {
    return false;
  }

  // Calculate the needed output buffer size.
  if (!configuration_processed_) {
    // FFmpeg's AVCodecContext's extradata field contains the Decoder
    // Specific Information from MP4 headers that contain the H.264 SPS and
    // PPS members. See ISO/IEC 14496-15 Chapter 5.2.4
    // AVCDecoderConfigurationRecord for exact specification.
    // Extradata must be at least 7 bytes long.
    if (stream_context_->extradata == NULL ||
        stream_context_->extradata_size <= 7) {
      return false;  // Can't go on with conversion without configuration.
    }
    configuration_size += converter_.ParseConfigurationAndCalculateSize(
        stream_context_->extradata,
        stream_context_->extradata_size);
    if (configuration_size == 0) {
      return false;  // Not possible to parse the configuration.
    }
  }
  uint32 output_nal_size =
      converter_.CalculateNeededOutputBufferSize(packet->data, packet->size);
  if (output_nal_size == 0) {
    return false;  // Invalid input packet.
  }
  output_packet_size = configuration_size + output_nal_size;

  // Allocate new packet for the output.
  AVPacket dest_packet;
  if (av_new_packet(&dest_packet, output_packet_size) != 0) {
    return false;  // Memory allocation failure.
  }
  // This is a bit tricky: since the interface does not allow us to replace
  // the pointer of the old packet with a new one, we will initially copy the
  // metadata from old packet to new bigger packet.
  dest_packet.pts = packet->pts;
  dest_packet.dts = packet->dts;
  dest_packet.pos = packet->pos;
  dest_packet.duration = packet->duration;
  dest_packet.convergence_duration = packet->convergence_duration;
  dest_packet.flags = packet->flags;
  dest_packet.stream_index = packet->stream_index;

  // Process the configuration if not done earlier.
  if (!configuration_processed_) {
    if (!converter_.ConvertAVCDecoderConfigurationRecordToByteStream(
            stream_context_->extradata, stream_context_->extradata_size,
            dest_packet.data, &configuration_size)) {
      return false;  // Failed to convert the buffer.
    }
    configuration_processed_ = true;
  }

  // Proceed with the conversion of the actual in-band NAL units, leave room
  // for configuration in the beginning.
  io_size = dest_packet.size - configuration_size;
  if (!converter_.ConvertNalUnitStreamToByteStream(
          packet->data, packet->size,
          dest_packet.data + configuration_size, &io_size)) {
    return false;
  }

  // At the end we must destroy the old packet.
  packet->destruct(packet);
  *packet = dest_packet;  // Finally, replace the values in the input packet.

  return true;
}

}  // namespace media

